{"record":{"id":"a7d2ca0012586bf1","repo":"immich-app/immich","slug":"forbidden","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":222,"sourceCode":"    const admin = await this.createUser({\n      isAdmin: true,\n      email: dto.email,\n      name: dto.name,\n      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","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L204-L240","documentation":"authenticate enforces route metadata after validating the request. If the resolved authDto.user is not an admin but the route is flagged adminRoute, access is denied with a 403 Forbidden (and a warning is logged with the denied URI). This is the admin-only route guard for the API.","triggerScenarios":"A non-admin authenticated user (session, API key, or shared link holder) calls an admin-only endpoint such as user management or server-config endpoints.","commonSituations":"Regular users hitting admin endpoints from scripts/UIs that assume elevated rights; an API key minted by a non-admin account; role downgraded to non-admin while an old client still calls admin routes.","solutions":["Perform the action with an administrator account or an API key created by an admin.","If the user should be admin, promote them via an existing admin (or server CLI) and retry.","Change the client to use the equivalent non-admin endpoint if one exists."],"exampleFix":"// before\nconst apiKey = createUserApiKey(nonAdminUser, 'key'); // 403 on admin routes\n// after\nconst apiKey = await adminApi.usersApi.createApiKey(adminUserId, 'admin-key');","handlingStrategy":"try-catch","validationCode":"const me = await api.usersApi.getMyUser();\nif (!me.isAdmin) {\n  throw new Error('Admin privileges required for this endpoint');\n}","typeGuard":"function isAdminUser(u: { isAdmin: boolean }): u is { isAdmin: true } {\n  return u.isAdmin === true;\n}","tryCatchPattern":"try {\n  await api.usersApi.getAllUsers(); // admin route\n} catch (e) {\n  if (e.status === 403) {\n    // surface 'admin account required' to the caller\n  }\n  throw e;\n}","preventionTips":["Check the user's isAdmin flag before calling admin endpoints.","Create admin-scoped API keys for administrative automation.","Handle role downgrades by refreshing stored user info."],"tags":["authorization","forbidden","admin"],"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"}