{"record":{"id":"503616e4c0330c7e","repo":"mastra-ai/mastra","slug":"state-token-has-expired-503616","errorCode":null,"errorMessage":"State token has expired","messagePattern":"State token has expired","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/clerk/src/index.ts","lineNumber":167,"sourceCode":" */\nasync function verifyStateToken(\n  stateToken: string,\n  secret: string,\n): Promise<{ originalState: string; redirectUri: string }> {\n  const parts = stateToken.split('.');\n  if (parts.length !== 2) {\n    throw new Error('Invalid state token format');\n  }\n\n  const [payloadB64, signature] = parts;\n  const expectedSig = await hmacSign(payloadB64!, secret);\n  if (!timingSafeEqual(signature!, expectedSig)) {\n    throw new Error('Invalid state token signature');\n  }\n\n  const payload = JSON.parse(atob(payloadB64!)) as StatePayload;\n  if (payload.e < Date.now()) {\n    throw new Error('State token has expired');\n  }\n\n  return { originalState: payload.s, redirectUri: payload.r };\n}\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n  return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Derive the Frontend API (FAPI) URL from a Clerk publishable key.\n * The publishable key is: prefix + base64(fapiDomain + \"$\")\n */\nfunction deriveFapiUrl(publishableKey: string): string {\n  const withoutPrefix = publishableKey.replace(/^pk_(test|live)_/, '');","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/clerk/src/index.ts#L149-L185","documentation":"The Clerk SSO state token is an HMAC-signed, base64-encoded JSON payload that embeds an expiry timestamp `e`. During callback verification, verifyStateToken re-parses the payload and compares the expiry against Date.now(); if the token's epoch-milliseconds have passed, the state can no longer be trusted as a fresh CSRF guard, so the library throws. This is an intentional liveness bound on the OAuth round-trip, not a signature or corruption problem.","triggerScenarios":"A user starts SSO login via _attachSSOProvider's getAuthorizationUri, which calls createStateToken with a TTL, but the provider's callback arrives after that TTL and redirectUri → verifyStateToken runs `payload.e < Date.now()` → throw.","commonSituations":"User leaves the login page open and resumes hours later; very long serverless cold starts or queues delaying the callback; clock skew between the node that signed the token and the node verifying it; an unusually short expiry configured for the state token.","solutions":["Have the user restart the SSO login flow so a fresh state token is issued.","Increase the state-token TTL via the provider's configuration/options if legitimate callbacks are slow.","Check server clocks (NTP) on all instances to eliminate skew between sign and verify.","Improve UX by handling the thrown error and redirecting the user back to the login initiation instead of showing a raw error."],"exampleFix":"// before: raw error surfaces on callback\nawait provider.verifyCallbackState(state);\n// after: restart flow on expiry\ntry {\n  await provider.verifyCallbackState(state);\n} catch (e) {\n  if ((e as Error).message === 'State token has expired') {\n    return res.redirect('/auth/sso/login'); // re-initiate\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"function isLikelyExpired(stateToken: string): boolean {\n  try {\n    const payload = JSON.parse(atob(stateToken.split('.')[0]));\n    return typeof payload.e === 'number' && payload.e < Date.now();\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await provider.verifyCallbackState(state);\n} catch (e) {\n  if ((e as Error).message === 'State token has expired') {\n    return restartLoginFlow(); // redirect to SSO initiation\n  }\n  throw e;\n}","preventionTips":["Set a state-token TTL comfortably longer than the slowest realistic OAuth round-trip.","Sync server clocks with NTP across all instances.","Auto-redirect to login re-initiation on expiry instead of surfacing a raw error."],"tags":["auth","oauth","token-expired","sso"],"backgroundTag":"oauth-state-token-expired","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}