{"record":{"id":"ff8301159ef380a8","repo":"mastra-ai/mastra","slug":"invalid-state-token-payload-ff8301","errorCode":null,"errorMessage":"Invalid state token payload","messagePattern":"Invalid state token payload","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"auth/google/src/auth-provider.ts","lineNumber":202,"sourceCode":"  stateToken: string,\n  secret: string,\n): Promise<{ originalState: string; redirectUri: string; nonce: 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  const expectedSig = await hmacSign(payloadB64, secret);\n  if (!timingSafeEqual(signature, expectedSig)) {\n    throw new Error('Invalid state token signature');\n  }\n\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    nonce: payload.n,\n  };\n}\n\nfunction hasExpired(payload: JWTPayload): boolean {\n  return typeof payload.exp === 'number' && payload.exp * 1000 < Date.now();\n}\n\nexport class MastraAuthGoogle extends MastraAuthProvider<GoogleUser> implements IUserProvider<GoogleUser> {","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/auth/google/src/auth-provider.ts#L184-L220","documentation":"verifyStateToken parses the base64-encoded payload portion of a signed state token. This error is thrown when JSON.parse fails on the decoded payload, meaning the state token payload is malformed or was truncated/corrupted in transit. The library throws it rather than returning invalid data because the state token protects the OAuth redirect flow against CSRF and tampering.","triggerScenarios":"Calling verifyStateToken (directly or via the SSO callback path) with a state string whose payload segment is not valid base64-encoded JSON — e.g. the state was truncated by a URL length limit, url-encoded twice, or the signature/payload separator was stripped.","commonSituations":"Proxy or middleware rewriting the callback URL and dropping part of the state query param; a client sending the state with '+' characters decoded as spaces in form-encoded bodies; hand-rolled state tokens from an older version of the library; concatenating the signed state with the server redirect-state suffix incorrectly.","solutions":["Pass the state parameter through unmodified: read it from the OAuth redirect request's query string and pass the raw value to verifyStateToken.","Check that the callback route/infra does not truncate or re-decode the state query parameter (double-decoding, body parsing, proxies).","Verify both ends use the same library version so createStateToken/verifyStateToken formats match; regenerate the login URL if the token came from an older format.","If the state has the server redirect-state suffix, ensure the full combined string is passed, not a partial slice."],"exampleFix":"// before (state mutated/re-decoded in route handler)\nconst state = decodeURIComponent(req.query.state as string);\nawait provider.verifyStateToken(state);\n\n// after (pass raw value)\nconst state = req.query.state as string;\nawait provider.verifyStateToken(state);","handlingStrategy":"try-catch","validationCode":"const isValidState = (s: unknown): s is string =>\n  typeof s === 'string' && s.length > 0 && /^[A-Za-z0-9+/=_-]+$/.test(s);","typeGuard":"function isNonEmptyState(v: unknown): v is string {\n  return typeof v === 'string' && v.length > 0;\n}","tryCatchPattern":"try {\n  const { originalState } = await provider.verifyStateToken(state);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Invalid state token payload') {\n    // reject callback: restart OAuth flow with a fresh login URL\n    return res.redirect('/login');\n  }\n  throw err;\n}","preventionTips":["Pass the state query parameter through to verifyStateToken completely unmodified — no extra decode/encode.","Audit proxies/middleware for query-string rewriting or truncation.","Never construct state strings manually; always use getLoginUrl output.","Keep createStateToken/verifyStateToken on the same library version."],"tags":["oauth","csrf","state-token","base64","parsing"],"backgroundTag":"invalid-oauth-state-token","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}