mastra-ai/mastra · error

Invalid or tampered state token

Error message

Invalid or tampered state token

What it means

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.

Solutions

  1. 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.
  2. Confirm multi-instance/multi-region deployments share the secret via the same secret store.
  3. Restart the login flow — a mismatched or expired token can never be verified; the user must begin a new OAuth authorize redirect.
  4. If this fires unexpectedly in production, audit for token tampering (potential CSRF) and log the event server-side without trusting the payload.
  5. Do not weaken or bypass the timing-safe comparison; verify against the exact payload string received.

Example fix

// before
const token = verifyStateToken(state, process.env.AUTH0_SECRET ?? 'dev-secret');
// after
const secret = process.env.AUTH0_SECRET;
if (!secret) throw new Error('AUTH0_SECRET not configured');
const token = verifyStateToken(state, secret);
Defensive patterns

Strategy: try-catch

Validate before calling

// No caller-side check can verify HMAC; instead ensure secret parity before the flow:
if (!process.env.AUTH0_SECRET) {
  throw new Error('Refusing to run OAuth flow: AUTH0_SECRET is not set');
}

Try / catch

try {
  const { originalState, redirectUri } = verifyStateToken(state, secret);
} catch (err) {
  if (err instanceof Error && err.message === 'Invalid or tampered state token') {
    log.warn('OAuth state signature mismatch — possible CSRF or secret rotation', { hasState: !!state });
    return new Response('Invalid OAuth state', { status: 400 });
  }
  throw err;
}

Prevention

When it happens

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

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

Related errors


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

Appendix: source

Thrown at auth/auth0/src/index.ts:130

  return `${payloadB64}.${signature}`;
}

/**
 * Verify and decode a state token.
 * Returns the original state and redirectUri if valid and not expired.
 */
function verifyStateToken(stateToken: string, secret: string): { originalState: string; redirectUri: string } {
  const parts = stateToken.split('.');
  if (parts.length !== 2) {
    throw new Error('Invalid state token format');
  }

  const [payloadB64, signature] = parts as [string, string];

  // Verify signature
  const expectedSig = hmacSign(payloadB64, secret);
  if (!timingSafeEqual(signature, expectedSig)) {
    throw new Error('Invalid or tampered state token');
  }

  // Decode and check expiry
  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,
  };

View on GitHub (pinned to 75dd419e61)