{"record":{"id":"e3c1067412edd8c9","repo":"toeverything/AFFiNE","slug":"action-forbidden-e3c106","errorCode":"action_forbidden","errorMessage":"You are not allowed to perform this action.","messagePattern":"You are not allowed to perform this action\\.","errorType":"exception","errorClass":"ActionForbidden","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/core/common/admin-guard.ts","lineNumber":30,"sourceCode":"@Injectable()\nexport class AdminGuard implements CanActivate, OnModuleInit {\n  private feature!: FeatureService;\n\n  constructor(private readonly ref: ModuleRef) {}\n\n  onModuleInit() {\n    this.feature = this.ref.get(FeatureService, { strict: false });\n  }\n\n  async canActivate(context: ExecutionContext) {\n    const { req } = getRequestResponseFromContext(context);\n    let allow = false;\n    if (req.session) {\n      allow = await this.feature.isAdmin(req.session.user.id);\n    }\n\n    if (!allow) {\n      throw new ActionForbidden();\n    }\n\n    return true;\n  }\n}\n\n/**\n * This guard is used to protect routes/queries/mutations that require a user to be administrator.\n *\n * @example\n *\n * ```typescript\n * \\@Admin()\n * \\@Mutation(() => UserType)\n * createAccount(userInput: UserInput) {\n *   // ...\n * }\n * ```","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/core/common/admin-guard.ts#L12-L48","documentation":"AdminGuard.canActivate (admin-guard.ts:30) throws ActionForbidden when the request's session user is not a platform administrator: allow stays false either because req.session is missing (unauthenticated/internal call) or feature.isAdmin(req.session.user.id) resolves false. It guards admin-only routes/queries/mutations with a hard 403-style failure.","triggerScenarios":"Calling any resolver/controller decorated with this admin guard while logged in as a non-admin user, or with no session at all; also when FeatureService is unavailable so isAdmin cannot confirm.","commonSituations":"Trying an admin GraphQL mutation or admin REST route from a normal member account on a self-hosted instance; scripts hitting admin endpoints with expired/missing auth; assuming workspace owner implies platform admin (it does not - this is instance-level admin).","solutions":["Sign in as a user flagged as administrator (instance-level admin, not merely workspace owner).","For self-hosted, add the user to the admin allowlist/flag in the feature/admin configuration and retry.","If you do not need admin features, call the regular non-guarded endpoint instead."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// server-side: check admin status before building admin responses\nconst isAdmin = req.session ? await featureService.isAdmin(req.session.user.id) : false;\nif (!isAdmin) {\n  return forbiddenFallback(); // hide admin entry points instead of triggering the guard\n}","typeGuard":"function isAdminForbidden(e: unknown): boolean {\n  return (\n    e instanceof GraphQLError &&\n    (e.extensions?.code === 'action_forbidden' ||\n      (e as HttpException)?.status === 403)\n  );\n}","tryCatchPattern":"try {\n  await adminMutation(input);\n} catch (e) {\n  if (isAdminForbidden(e)) {\n    // hide admin UI and inform the user admin rights are required; never retry\n  } else throw e;\n}","preventionTips":["Gate admin UI/features on a session admin flag fetched up front.","Remember this is instance-level admin, not workspace owner.","On self-hosted, ensure the admin user is flagged before wiring admin clients/scripts."],"tags":["authorization","admin","rbac","guard","forbidden"],"backgroundTag":"permission-denied","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}