{"record":{"id":"556615bb6b7afcc5","repo":"mastra-ai/mastra","slug":"invalid-or-tampered-state-token","errorCode":null,"errorMessage":"Invalid or tampered state token","messagePattern":"Invalid or tampered state token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/auth0/src/index.ts","lineNumber":130,"sourceCode":"  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 */\nfunction verifyStateToken(stateToken: string, secret: string): { 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 as [string, string];\n\n  // Verify signature\n  const expectedSig = hmacSign(payloadB64, secret);\n  if (!timingSafeEqual(signature, expectedSig)) {\n    throw new Error('Invalid or tampered state token');\n  }\n\n  // Decode and check expiry\n  let payload: StatePayload;\n  try {\n    payload = JSON.parse(atob(payloadB64)) as StatePayload;\n  } catch {\n    throw new Error('Invalid state token payload');\n  }\n\n  if (payload.e < Date.now()) {\n    throw new Error('State token has expired');\n  }\n\n  return {\n    originalState: payload.s,\n    redirectUri: payload.r,\n  };","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/auth0/src/index.ts#L112-L148","documentation":"After splitting the state token, verifyStateToken recomputes the HMAC-SHA256 signature of the payload with the server secret and compares it to the signature in the token using a timing-safe comparison. If they differ, the payload was modified, was signed with a different secret, or is not from this server — so it throws 'Invalid or tampered state token' to prevent CSRF and open-redirect via a forged redirectUri.","triggerScenarios":"Calling verifyStateToken with a token whose signature part doesn't match hmacSign(payloadB64, secret): hand-edited payload, token generated with a different/rotated secret, or a token copied from another environment (staging token verified in production).","commonSituations":"SESSION/AUTH secret differs between environments or changed between the login redirect and callback (deploy, secret rotation, missing env var falling back to a default); multi-instance deployments without a shared secret; an attacker-supplied state in a CSRF attempt (this error is the protection working).","solutions":["Ensure the same signing secret is configured on both the login endpoint and the callback endpoint (same env var, same value) and was not rotated mid-flow.","Confirm multi-instance/multi-region deployments share the secret via the same secret store.","Restart the login flow — a mismatched or expired token can never be verified; the user must begin a new OAuth authorize redirect.","If this fires unexpectedly in production, audit for token tampering (potential CSRF) and log the event server-side without trusting the payload.","Do not weaken or bypass the timing-safe comparison; verify against the exact payload string received."],"exampleFix":"// before\nconst token = verifyStateToken(state, process.env.AUTH0_SECRET ?? 'dev-secret');\n// after\nconst secret = process.env.AUTH0_SECRET;\nif (!secret) throw new Error('AUTH0_SECRET not configured');\nconst token = verifyStateToken(state, secret);","handlingStrategy":"try-catch","validationCode":"// No caller-side check can verify HMAC; instead ensure secret parity before the flow:\nif (!process.env.AUTH0_SECRET) {\n  throw new Error('Refusing to run OAuth flow: AUTH0_SECRET is not set');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { originalState, redirectUri } = verifyStateToken(state, secret);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Invalid or tampered state token') {\n    log.warn('OAuth state signature mismatch — possible CSRF or secret rotation', { hasState: !!state });\n    return new Response('Invalid OAuth state', { status: 400 });\n  }\n  throw err;\n}","preventionTips":["Use one shared, persistent signing secret across all instances and environments; never fall back to defaults.","Plan secret rotation to leave old tokens invalid — send users to a fresh login rather than failing mid-callback.","Log signature mismatches server-side as potential CSRF signals.","Never log or echo the token payload on failure (it is untrusted)."],"tags":["auth0","oauth","csrf","hmac","signature-verification"],"backgroundTag":"hmac-signature-mismatch","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}