mastra-ai/mastra · error
State token has expired
Error message
State token has expired
What it means
verifyStateToken parses a signed, base64-encoded state token created during SSO login and returns the original state and redirect URI. This error is thrown when the token's embedded expiry timestamp (payload.e) is in the past, i.e. the login flow took too long between generating the state token (getLoginUrl) and verifying it in the callback. The library expires these state tokens to prevent replay attacks and stale redirect URIs.
Source
Thrown at auth/auth0/src/index.ts:142
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,
};
}
/**
* Simple HMAC-SHA256 using Web Crypto (sync wrapper for predictable use).
* Returns base64url-encoded signature.
*/
function hmacSign(data: string, secret: string): string {
// Use a simple hash-based approach that works synchronously
// This is a simplified HMAC for state tokens (not for long-term secrets)
const encoder = new TextEncoder();
const keyBytes = encoder.encode(secret);
const dataBytes = encoder.encode(data);View on GitHub (pinned to 75dd419e61)
Solutions
- Restart the login flow: call getLoginUrl again to generate a fresh signed state token and redirect the user to it
- Handle this error in the SSO callback handler by redirecting the user to a fresh login URL instead of failing hard
- Check for clock skew (NTP) on the server if expiries appear to happen prematurely
- Increase the state token TTL in the auth server config only if the login flow legitimately needs longer than the current window
Example fix
// before
const { originalState, redirectUri } = authServer.redirectUri(state);
// after
let redirect;
try {
redirect = authServer.redirectUri(state);
} catch (e) {
if (e.message === 'State token has expired') {
return Response.redirect(authServer.getLoginUrl('/callback', newState()), 302);
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// State token content is signed+encoded; expiry cannot be checked beforehand without decoding.
// Detect staleness by decoding payload if you control token creation:
function isStateTokenLikelyExpired(signedState: string, ttlMs: number, issuedAt: number) {
return Date.now() - issuedAt > ttlMs;
} Try / catch
try {
const { originalState, redirectUri } = provider.redirectUri(state);
} catch (e) {
if (e instanceof Error && e.message === 'State token has expired') {
// restart login flow
return redirectTo(provider.getLoginUrl(callbackUri, newState()));
}
throw e;
} Prevention
- Always treat state-token expiry as 'restart login' rather than a fatal error
- Keep the time between generating the login URL and completing the callback short
- Never cache or bookmark state tokens/callback URLs
- Sync server clocks with NTP to avoid premature expiry
When it happens
Trigger: Calling redirectUri() (which calls verifyStateToken) on a state token whose payload.e is less than Date.now(); typically when the user sits on the Auth0 login page longer than the token TTL before completing login, or when a bookmarked/stale callback URL is replayed after expiry.
Common situations: Users leaving the Auth0 consent screen open past the expiry window; replaying an old callback URL from browser history; long-running approval flows in enterprise SSO; clock skew between server instances if system clocks drift.
Related errors
- Cookie password must be at least 32 characters for SSO. Set
- Redirect URI is required for SSO. Set AUTH0_REDIRECT_URI or
- Invalid encrypted session data
- Invalid state token format
- Invalid or tampered state token
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/4a8a3b23589424cf.
Report an issue: GitHub.