{"record":{"id":"69bc64fa4c7133cc","repo":"immich-app/immich","slug":"denied-access-to-non-shared-route-uri","errorCode":null,"errorMessage":"Denied access to non-shared route: ${uri}","messagePattern":"Denied access to non-shared route: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/services/auth.service.ts","lineNumber":226,"sourceCode":"      password: dto.password,\n      storageLabel: 'admin',\n    });\n\n    return mapUserAdmin(admin);\n  }\n\n  async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise<AuthDto> {\n    const authDto = await this.validate({ headers, queryParams });\n    const { adminRoute, sharedLinkRoute, uri } = metadata;\n    const requestedPermission = metadata.permission ?? Permission.All;\n\n    if (!authDto.user.isAdmin && adminRoute) {\n      this.logger.warn(`Denied access to admin only route: ${uri}`);\n      throw new ForbiddenException('Forbidden');\n    }\n\n    if (authDto.sharedLink && !sharedLinkRoute) {\n      this.logger.warn(`Denied access to non-shared route: ${uri}`);\n      throw new ForbiddenException('Forbidden');\n    }\n\n    if (\n      authDto.apiKey &&\n      requestedPermission !== false &&\n      !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })\n    ) {\n      throw new ForbiddenException(`Missing required permission: ${requestedPermission}`);\n    }\n\n    return authDto;\n  }\n\n  private async validate({ headers, queryParams }: Omit<ValidateRequest, 'metadata'>): Promise<AuthDto> {\n    const shareKey = (headers[ImmichHeader.SharedLinkKey] || queryParams[ImmichQuery.SharedLinkKey]) as string;\n    const shareSlug = (headers[ImmichHeader.SharedLinkSlug] || queryParams[ImmichQuery.SharedLinkSlug]) as string;\n    const session = (headers[ImmichHeader.UserToken] ||","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L208-L244","documentation":"When a request is authenticated via a shared link, access is restricted to routes explicitly marked sharedLinkRoute. If a shared-link session attempts any other route, this warning is logged with the URI and a ForbiddenException is thrown. It prevents shared-link visitors from using general API surface.","triggerScenarios":"A request authenticated with a shared link (authDto.sharedLink set) targets a route whose metadata does not include sharedLinkRoute=true.","commonSituations":"Shared-link page code or a manual client calling non-shared endpoints (e.g. /api/users, albums API) with shared-link credentials; stale clients probing routes with link tokens.","solutions":["Ensure the shared-link frontend only calls shared-link-permitted endpoints.","Use a full authenticated user session/API key instead of a shared link for those endpoints.","Check custom integrations: shared links cannot act as general API credentials.","If a legitimate shared route is blocked, verify the route metadata is flagged sharedLinkRoute."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// use only shared-link-permitted endpoints when authenticated via a shared link\nif (authMethod === 'shared-link') {\n  assert(allowedSharedRoutes.has(endpoint), `${endpoint} is not accessible via shared link`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.call(endpoint);\n} catch (e) {\n  if (e instanceof ForbiddenException && usingSharedLink) {\n    showToast('Shared links can only access shared content');\n  } else throw e;\n}","preventionTips":["Treat shared links as scoped read-only credentials, not API keys.","Restrict shared-link clients to endpoints explicitly allowed for shared links.","For broader access, authenticate as a full user instead.","Review custom integrations for shared-link token leakage into general endpoints."],"tags":["authorization","shared-links","security"],"backgroundTag":"permission-denied","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}