{"record":{"id":"56637e77612e6a61","repo":"mastra-ai/mastra","slug":"invalid-state-token-format-56637e","errorCode":null,"errorMessage":"Invalid state token format","messagePattern":"Invalid state token format","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"auth/clerk/src/index.ts","lineNumber":156,"sourceCode":"    r: redirectUri,\n    e: Date.now() + STATE_TOKEN_EXPIRY_MS,\n  };\n  const payloadB64 = btoa(JSON.stringify(payload));\n  const signature = await hmacSign(payloadB64, secret);\n  return `${payloadB64}.${signature}`;\n}\n\n/**\n * Verify and decode a state token.\n * Returns the original state and redirectUri if valid and not expired.\n */\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.","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/clerk/src/index.ts#L138-L174","documentation":"Clerk OAuth state tokens in this provider are signed strings of the form `<payloadB64>.<hmacSignature>`; verifyStateToken splits on '.' and requires exactly two parts before verifying the HMAC. A token not matching `payload.signature` means it was corrupted, truncated, hand-crafted, or was never issued by this provider's createStateToken.","triggerScenarios":"verifyStateToken(stateToken, secret) where stateToken.split('.').length !== 2 — callback query param ?state= missing its signature part, double-encoding mangling the dot, or an attacker/random string supplied as state in the OAuth redirect callback.","commonSituations":"Reverse proxies or frameworks re-encoding the state query parameter; client-side code trimming/altering the state value; an attacker probing the OAuth callback endpoint (this check correctly rejects them); copying a state from a different environment.","solutions":["Restart the OAuth flow: generate a fresh state via the provider's redirect flow and use it unmodified in the callback.","Ensure the state query parameter is passed through URL encoding unchanged — do not decode/re-encode or append query params to it.","Verify the same state is round-tripped: the callback URL used by Clerk must be the one generated by the provider."],"exampleFix":"// before: manually rebuilding the callback URL and dropping part of state\nconst url = `/auth/callback?state=${state.split('.')[0]}`;\n\n// after: pass state through verbatim\nconst url = `/auth/callback?state=${encodeURIComponent(state)}`;","handlingStrategy":"try-catch","validationCode":"const state = new URL(callbackUrl).searchParams.get('state');\nif (!state || state.split('.').length !== 2) {\n  return respondBadRequest('state parameter is malformed; restart the OAuth flow');\n}","typeGuard":"function isWellFormedStateToken(state: string | null | undefined): state is string {\n  return typeof state === 'string' && /^[A-Za-z0-9+/=_-]+\\.[A-Za-z0-9+/=_-]+$/.test(state);\n}","tryCatchPattern":"try {\n  const { redirectUri } = await verifyStateToken(state, secret);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid state token format') {\n    return respondBadRequest('state mismatch — restart OAuth sign-in');\n  }\n  throw e;\n}","preventionTips":["Pass the state query parameter through verbatim; never parse, trim, or rebuild it.","Restart the OAuth flow whenever state validation fails instead of retrying the stale callback.","Ensure proxies don't rewrite query strings on the OAuth callback route."],"tags":["oauth","csrf","clerk","token-validation"],"backgroundTag":"invalid-csrf-state-token","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}