vxcontrol/pentagi · error
nonce mismatch in Google ID Token claims
Error message
nonce mismatch in Google ID Token claims
What it means
Thrown when the nonce claim inside the decoded Google ID Token does not equal the nonce value that PentAGI generated and embedded in the OAuth state at authorization start. This is an anti-replay check: it proves the ID token returned in the code exchange corresponds to this exact login attempt, not a replayed or cross-request token. Note that idToken.Nonce is already checked at line 36 — this second check validates the nonce found in the raw claims payload.
Source
Thrown at backend/pkg/server/oauth/google.go:50
if err != nil {
return "", false, fmt.Errorf("could not verify Google ID Token: %w", err)
}
if idToken.Nonce != nonce {
return "", false, fmt.Errorf("nonce mismatch in Google ID Token")
}
if err = idToken.VerifyAccessToken(token.AccessToken); err != nil {
return "", false, fmt.Errorf("failed to verify Google Access Token: %w", err)
}
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w", err)
}
if claims.Nonce != nonce {
return "", false, fmt.Errorf("nonce mismatch in Google ID Token claims")
}
if claims.Email == "" {
return "", false, fmt.Errorf("email is empty in Google ID Token claims")
}
return claims.Email, claims.EmailVerified, nil
}
}
func NewGoogleOAuthClient(clientID, clientSecret, redirectURL string) OAuthClient {
return NewOAuthClient("google", &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: redirectURL,
Scopes: []string{
"https://www.googleapis.com/auth/userinfo.email",
"openid",View on GitHub (pinned to ea665308ba)
Solutions
- Have the user retry the login from a single tab and clear any stale OAuth state cookies.
- Verify the state cookie handling: the nonce used at /auth/authorize must be the same one carried through state to the callback (check for cookie name collisions across tabs).
- Do not cache or reuse authorization codes/states; each login must generate a fresh nonce.
- If you see this consistently, log both expected and received nonce to identify which side (state cookie vs token) is stale.
- Ensure any reverse proxy is not caching the callback URL response.
Defensive patterns
Strategy: validation
Validate before calling
// before initiating login, ensure one state/nonce per attempt and no concurrent sessions sharing the state cookie
state, nonce, err := newOAuthState(userID) // fresh random nonce per attempt
if err != nil { return err }
// never reuse a state value; bind it to a single-use cookie with Max-Age ~10m Type guard
func nonceMatches(expected, claimsNonce string) bool {
return expected != "" && claimsNonce != "" && subtle.ConstantTimeCompare([]byte(expected), []byte(claimsNonce)) == 1
} Try / catch
email, verified, err := resolver.Resolve(ctx, nonce, token)
if err != nil {
if strings.Contains(err.Error(), "nonce mismatch") {
return fmt.Errorf("login attempt expired or replayed — please start a new sign-in")
}
return err
} Prevention
- Generate a fresh nonce for every authorization attempt
- Mark state values single-use and expire them quickly
- Avoid multiple simultaneous login tabs sharing one session cookie
- Never cache or replay authorization codes/states
When it happens
Trigger: AuthAuthorize callback flow: the state-derived nonce differs from claims.Nonce — e.g. the user completed the OAuth flow in a second browser tab/attempt so the callback state belongs to an older authorization request, or the same code/state pair was replayed.
Common situations: User opens the login link twice (two in-flight states, callback uses stale state cookie); browser back-button resubmitting an old callback; multiple concurrent login sessions overwriting the state cookie; clock/retry issues causing the token exchange to run twice.
Related errors
- nonce mismatch in Google ID Token
- could not create Google OpenID client: %w
- id_token is not present in the token
- could not verify Google ID Token: %w
- failed to verify Google Access Token: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/38ca2cf5937509cf.
Report an issue: GitHub.