{"record":{"id":"4a03edc4a6d9032e","repo":"toeverything/AFFiNE","slug":"authentication-required-4a03ed","errorCode":"authentication_required","errorMessage":"You must sign in first to access this resource.","messagePattern":"You must sign in first to access this resource\\.","errorType":"exception","errorClass":"AuthenticationRequired","httpStatus":401,"severity":"error","filePath":"packages/backend/server/src/core/auth/guard.ts","lineNumber":75,"sourceCode":"\n  async canActivate(context: ExecutionContext) {\n    const { req, res } = getRequestResponseFromContext(context);\n    const clazz = context.getClass();\n    const handler = context.getHandler();\n    // api is public\n    const isPublic = this.reflector.getAllAndOverride<boolean>(\n      PUBLIC_ENTRYPOINT_SYMBOL,\n      [clazz, handler]\n    );\n\n    const authedUser = await this.signIn(req, res, isPublic);\n\n    if (isPublic) {\n      return true;\n    }\n\n    if (!authedUser) {\n      throw new AuthenticationRequired();\n    }\n\n    return true;\n  }\n\n  async signIn(\n    req: Request,\n    res?: Response,\n    isPublic = false\n  ): Promise<Session | null> {\n    const result = await this.resolveRequestSession(req, res, isPublic);\n    return result?.session ?? null;\n  }\n\n  private async resolveRequestSession(\n    req: Request,\n    res?: Response,\n    isPublic = false","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/591f874dad30887a80143a061a44bd3ca7ee3299/packages/backend/server/src/core/auth/guard.ts#L57-L93","documentation":"The global AuthGuard rejects any route that is not decorated @Public() when no authenticated user could be resolved. signIn() tries the Authorization: Bearer JWT path first (only if the bearer looks like a JWT) and then the affine_session cookie; if neither yields a session, AuthenticationRequired (authentication_required, HTTP 401) is thrown before the handler runs.","triggerScenarios":"Calling a protected REST endpoint or GraphQL mutation with no cookies and no bearer token; an expired or revoked affine_session cookie; a bearer string that is not JWT-shaped (falls through to the cookie path); cross-origin fetch that did not send cookies; WebSocket/SSE handshake without credentials.","commonSituations":"Session expired while the tab was open; server restart with a new auth secret invalidating cookies; frontend calling the API before sign-in finishes; axios/fetch missing withCredentials; devtools 'copy as fetch' losing the cookie header.","solutions":["Sign in first, or attach Authorization: Bearer <auth-session access token> to the request","For cookie auth always send credentials (fetch credentials: 'include' / axios withCredentials: true)","Handle 401 in an interceptor: refresh the session, then retry once or redirect to sign-in","If sessions died after a server config change, check that the auth secret / cookie settings are stable across restarts"],"exampleFix":"// before\nawait fetch('/api/auth/sessions'); // no credentials\n\n// after\nconst res = await fetch('/api/auth/sessions', {\n  credentials: 'include',\n  headers: accessToken ? { authorization: `Bearer ${accessToken}` } : {},\n});\nif (res.status === 401) location.href = '/sign-in';","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAuthenticationRequired(e: unknown): boolean {\n  return (\n    typeof e === 'object' && e !== null &&\n    (e as { code?: string }).code === 'authentication_required'\n  );\n}","tryCatchPattern":"try {\n  return await api.get('/auth/sessions');\n} catch (e) {\n  if (isAuthenticationRequired(e)) {\n    redirectToSignIn(); // or refresh session then retry once\n    return null;\n  }\n  throw e;\n}","preventionTips":["Install a global 401 interceptor instead of catching per call","Send credentials on every authenticated request from one shared client","Check session presence (user object) before rendering authenticated UI"],"tags":["auth","session","authentication","http-401"],"backgroundTag":"unauthenticated-request","analyzedSha":"591f874dad30887a80143a061a44bd3ca7ee3299","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}