mastra-ai/mastra · error

Invalid state token payload

Error message

Invalid state token payload

What it means

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.

Source

Thrown at auth/google/src/auth-provider.ts:202

  stateToken: string,
  secret: string,
): Promise<{ originalState: string; redirectUri: string; nonce: string }> {
  const parts = stateToken.split('.');
  if (parts.length !== 2) {
    throw new Error('Invalid state token format');
  }

  const [payloadB64, signature] = parts as [string, string];
  const expectedSig = await hmacSign(payloadB64, secret);
  if (!timingSafeEqual(signature, expectedSig)) {
    throw new Error('Invalid state token signature');
  }

  let payload: StatePayload;
  try {
    payload = JSON.parse(atob(payloadB64)) as StatePayload;
  } catch {
    throw new Error('Invalid state token payload');
  }

  if (payload.e < Date.now()) {
    throw new Error('State token has expired');
  }

  return {
    originalState: payload.s,
    redirectUri: payload.r,
    nonce: payload.n,
  };
}

function hasExpired(payload: JWTPayload): boolean {
  return typeof payload.exp === 'number' && payload.exp * 1000 < Date.now();
}

export class MastraAuthGoogle extends MastraAuthProvider<GoogleUser> implements IUserProvider<GoogleUser> {

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass the state parameter through unmodified: read it from the OAuth redirect request's query string and pass the raw value to verifyStateToken.
  2. Check that the callback route/infra does not truncate or re-decode the state query parameter (double-decoding, body parsing, proxies).
  3. 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.
  4. If the state has the server redirect-state suffix, ensure the full combined string is passed, not a partial slice.

Example fix

// before (state mutated/re-decoded in route handler)
const state = decodeURIComponent(req.query.state as string);
await provider.verifyStateToken(state);

// after (pass raw value)
const state = req.query.state as string;
await provider.verifyStateToken(state);
Defensive patterns

Strategy: try-catch

Validate before calling

const isValidState = (s: unknown): s is string =>
  typeof s === 'string' && s.length > 0 && /^[A-Za-z0-9+/=_-]+$/.test(s);

Type guard

function isNonEmptyState(v: unknown): v is string {
  return typeof v === 'string' && v.length > 0;
}

Try / catch

try {
  const { originalState } = await provider.verifyStateToken(state);
} catch (err) {
  if (err instanceof Error && err.message === 'Invalid state token payload') {
    // reject callback: restart OAuth flow with a fresh login URL
    return res.redirect('/login');
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/ff8301159ef380a8. Report an issue: GitHub.