{"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/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/core/auth/open-app.ts#L9-L33","documentation":"OpenAppService.verifySignInCode consumes a one-time challenge of type 'open_app_sign_in' (created with a 5-minute TTL) and throws InvalidAuthState (invalid_auth_state) when consumption returns no payload or the payload lacks a userId - i.e. the code from the web 'open in app' flow is unknown, expired, or already used.","triggerScenarios":"Entering/scanning an open-app code more than 5 minutes after it was generated; re-submitting a code that already succeeded (challenges are single-use); a mistyped or truncated code; the challenge record expiring server-side before submission.","commonSituations":"Users letting the QR/code screen sit before scanning; retries after network failures double-submitting the same code; copy/paste losing characters; clock skew between issuing and consuming services.","solutions":["Generate a fresh code in the web app and resubmit immediately (within 5 minutes)","Never retry the same code after a failure - always start a new challenge","Copy the full code without truncation","Check the device clock if freshly generated codes still fail"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const OPEN_APP_CODE_TTL_MS = 5 * 60 * 1000;\nfunction isCodeStillValid(issuedAt: number): boolean {\n  return Date.now() - issuedAt < OPEN_APP_CODE_TTL_MS;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await verifySignInCode(code);\n} catch (e) {\n  if (isAffineErrorCode(e, 'invalid_auth_state')) {\n    const fresh = await generateNewOpenAppCode(); // codes are single-use, 5-minute TTL\n    await verifySignInCode(fresh);\n  } else throw e;\n}","preventionTips":["Submit open-app codes immediately after generation; never reuse them","On any failure, restart the challenge instead of retrying the same code"],"tags":["auth","open-app","challenge-code","otp","expiry"],"backgroundTag":"one-time-code-expired","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"}