{"record":{"id":"12920541ba62e9a9","repo":"toeverything/AFFiNE","slug":"authentication-required-129205","errorCode":"authentication_required","errorMessage":"You must sign in first to access this resource.","messagePattern":"You must sign in first to access this resource\\.","errorType":"http","errorClass":"AuthenticationRequired","httpStatus":401,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/resolvers/member.ts","lineNumber":711,"sourceCode":"    _workspaceId: string,\n    @Args('sendAcceptMail', {\n      nullable: true,\n      deprecationReason: 'never used',\n    })\n    _sendAcceptMail: boolean\n  ) {\n    const role = await this.models.workspaceUser.getById(inviteId);\n    // invitation by email\n    if (role) {\n      if (user && user.id !== role.userId) {\n        throw new InvalidInvitation();\n      }\n\n      await this.acceptInvitationByEmail(role);\n    } else {\n      // invitation by link\n      if (!user) {\n        throw new AuthenticationRequired();\n      }\n\n      const invitation = await this.cache.get<{\n        workspaceId: string;\n        inviterUserId: string;\n      }>(`workspace:inviteLinkId:${inviteId}`);\n\n      if (!invitation) {\n        throw new InvalidInvitation();\n      }\n\n      const role = await this.models.workspaceUser.get(\n        invitation.workspaceId,\n        user.id\n      );\n\n      if (role) {\n        // if status is pending, should accept the invitation directly","sourceCodeStart":693,"sourceCodeEnd":729,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/resolvers/member.ts#L693-L729","documentation":"Thrown by `acceptInviteById` on the link-invitation branch when the mutation is invoked without an authenticated user. Link invites require a session because the resulting membership is bound to the caller's id; the mutation is marked `@Public()` only so that email-invite guests get a clearer error than a generic auth failure. Coded `authentication_required`.","triggerScenarios":"Calling `acceptInviteById` with a link invite id (no matching `workspace_user` row) while `user` is `undefined` — e.g. an unauthenticated browser landing on the invite-accept route, or a client that did not attach the session token.","commonSituations":"Session expired between viewing the invite page and clicking accept; the auth cookie was blocked by third-party cookie restrictions; an unauthenticated user opened the link directly; misconfigured auth gateway stripping headers.","solutions":["Redirect unauthenticated users to sign-in with a return URL pointing back to the invite.","Ensure the auth token/cookie is attached to the GraphQL request (credentials: 'include' on the fetcher).","Refresh the session before retrying if it may have expired.","For link invites, detect the unauthenticated state up front and send the user through the sign-in flow."],"exampleFix":"// before\nawait sdk.acceptInviteById({ inviteId }); // link invite, no session\n\n// after\nif (!currentUser) {\n  router.push(`/signin?redirect=${encodeURIComponent('/invite/' + inviteId)}`);\n  return;\n}\nawait sdk.acceptInviteById({ inviteId });","handlingStrategy":"validation","validationCode":"// Require a session for link invites\nif (!currentUser) {\n  router.push(`/signin?redirect=${encodeURIComponent('/invite/' + inviteId)}`);\n  return;\n}\nawait sdk.acceptInviteById({ inviteId });","typeGuard":"function hasSession(user) {\n  return Boolean(user && user.id);\n}","tryCatchPattern":"try {\n  await sdk.acceptInviteById({ inviteId });\n} catch (e) {\n  if (e.code === 'authentication_required') {\n    redirectToSignIn(location.pathname);\n  } else throw e;\n}","preventionTips":["Ensure the auth cookie/token is attached to GraphQL requests (credentials: 'include').","Redirect unauthenticated users to sign-in with a return URL before accepting.","Detect session expiry client-side and re-authenticate before retrying."],"tags":["graphql","workspace","invitation","authentication","nestjs"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}