mastra-ai/mastra · error
Invalid state token format
Error message
Invalid state token format
What it means
OAuth state tokens are stateless HMAC-signed strings of the form 'base64(payload).base64(signature)'. verifyStateToken splits on '.' and requires exactly two parts; anything else cannot be a well-formed signed token, so it throws 'Invalid state token format' before any signature check.
Source
Thrown at auth/auth0/src/index.ts:122
function createStateToken(originalState: string, redirectUri: string, secret: string): string {
const payload: StatePayload = {
s: originalState,
r: redirectUri,
e: Date.now() + STATE_TOKEN_EXPIRY_MS,
};
const payloadB64 = btoa(JSON.stringify(payload));
const signature = hmacSign(payloadB64, secret);
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');
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Only verify state tokens produced by createStateToken of this library — raw state values and JWTs will never have the payload.signature shape.
- Ensure the state parameter is passed through the OAuth round trip verbatim (no extra encode/decode) between the login redirect and the callback handler.
- Check your callback route reads the 'state' query param, not another parameter or a merged value.
- Clear stale bookmarks/links with old state and start a fresh login flow.
- Wrap verification in try/catch and reject the callback (400) without leaking which check failed.
Example fix
// before
const { redirectUri } = verifyStateToken(searchParams.get('state') ?? '', secret);
// after
const state = searchParams.get('state');
if (!state || state.split('.').length !== 2) {
return new Response('Bad state', { status: 400 });
}
const { redirectUri } = verifyStateToken(state, secret); Defensive patterns
Strategy: validation
Validate before calling
function isSignedStateToken(v) {
return typeof v === 'string' && v.length > 0 && v.split('.').length === 2;
}
const state = url.searchParams.get('state');
if (!isSignedStateToken(state)) return new Response('Bad Request', { status: 400 }); Type guard
function isStateToken(v: unknown): v is string {
return typeof v === 'string' && /^[A-Za-z0-9+/=]+\.[A-Za-z0-9+/=]+$/.test(v);
} Try / catch
try {
const { originalState, redirectUri } = verifyStateToken(state, secret);
} catch (err) {
if (err instanceof Error && err.message === 'Invalid state token format') {
return new Response('Invalid OAuth state', { status: 400 });
}
throw err;
} Prevention
- Only feed tokens created by createStateToken into verifyStateToken.
- Pass the OAuth state param through the redirect/callback verbatim — no extra decoding.
- Never mix state formats from other auth libraries in the same flow.
- Reject malformed state early with a 400 before hitting the crypto path.
When it happens
Trigger: Passing a state value to verifyStateToken that contains zero or more than one '.' — e.g. an OAuth state generated by another library (JWTs contain dots), a raw random string without a signature, a double-encoded or re-URL-encoded token where '.' was mangled, or an empty string.
Common situations: Mixing CSRF state from a different auth library or a previous implementation; the round-tripped state was URL-decoded/transformed by a framework or callback handler before verification; user bookmarks an old login URL and the expired/mangled state is replayed; hand-crafting state in tests.
Related errors
- Invalid or tampered state token
- Invalid state token payload
- Invalid state token payload
- State token has expired
- Invalid encrypted session data
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/483d8a6a34842a84.
Report an issue: GitHub.