{"record":{"id":"d9fe744f3cfc7808","repo":"toeverything/AFFiNE","slug":"invalid-auth-state-d9fe74","errorCode":"invalid_auth_state","errorMessage":"Invalid auth state. You might start the auth progress from another device.","messagePattern":"Invalid auth state\\. You might start the auth progress from another device\\.","errorType":"exception","errorClass":"InvalidAuthState","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/auth/open-app.ts","lineNumber":27,"sourceCode":"export class OpenAppAuthService {\n  constructor(private readonly challenges: AuthChallengeStore) {}\n\n  async createSignInCode(user: CurrentUser) {\n    return this.challenges.create(\n      'open_app_sign_in',\n      { userId: user.id },\n      5 * 60 * 1000\n    );\n  }\n\n  async verifySignInCode(code: string): Promise<VerifiedIdentity> {\n    const payload = await this.challenges.consume<{ userId?: string }>(\n      'open_app_sign_in',\n      code\n    );\n\n    if (!payload?.userId) {\n      throw new InvalidAuthState();\n    }\n\n    return { userId: payload.userId, method: 'open_app' };\n  }\n}\n","sourceCodeStart":9,"sourceCodeEnd":33,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/auth/open-app.ts#L9-L33","documentation":"OpenAppAuthService.verifySignInCode consumes the 'open_app_sign_in' challenge (a short-lived 5-minute single-use code created by createSignInCode). If consume returns null/undefined or a payload without a userId, InvalidAuthState is thrown. This means the code is missing, expired, already consumed, or was malformed — there is no valid user identity to authenticate.","triggerScenarios":"verifySignInCode(code) called with a code that does not exist, was already consumed (one-time use), or is past its 5-minute TTL — so challenges.consume returns no payload or a payload lacking userId.","commonSituations":"User let the QR/deep-link sign-in code expire before confirming. The same code was submitted twice (double-tap). The challenge store (Redis/cache) was flushed, dropping the code. A code generated for a different purpose was passed.","solutions":["Generate a fresh sign-in code via createSignInCode and complete verification within 5 minutes.","Ensure verify is called exactly once per code; request a new code on any failure.","Confirm the challenge store backend is running and not being cleared mid-flow.","Pass the code value verbatim from createSignInCode without truncation."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isInvalidAuthState(err: unknown): boolean {\n  return (\n    !!err &&\n    typeof err === 'object' &&\n    (err as { code?: string }).code === 'invalid_auth_state'\n  );\n}","tryCatchPattern":"try {\n  await openApp.verifySignInCode(code);\n} catch (err) {\n  if (isInvalidAuthState(err)) {\n    code = await openApp.createSignInCode(user); // refresh\n    return;\n  }\n  throw err;\n}","preventionTips":["Consume the open-app sign-in code within 5 minutes of creation.","Use each code exactly once; regenerate on any failure.","Ensure the challenge store (cache/redis) is healthy and not flushed mid-flow.","Pass the code verbatim from createSignInCode without truncation."],"tags":["auth","open-app","challenge-store","expired-token","one-time-code"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}