{"record":{"id":"5c5eda5481e31461","repo":"xai-org/grok-build","slug":"noncemismatch","errorCode":"NonceMismatch","errorMessage":"OidcError::NonceMismatch","messagePattern":"OidcError::NonceMismatch","errorType":"error_code","errorClass":"OidcError","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs","lineNumber":692,"sourceCode":"    validation.set_issuer(&[expected_issuer]);\n    validation.set_audience(&[expected_client_id]);\n    validation.validate_exp = true;\n    validation.validate_aud = true;\n    validation.required_spec_claims = [\"sub\", \"iss\", \"aud\", \"exp\"]\n        .into_iter()\n        .map(ToOwned::to_owned)\n        .collect();\n    let token_data = jsonwebtoken::decode::<IdTokenClaims>(token, &decoding_key, &validation)?;\n    if token_data.claims.iss.as_deref() != Some(expected_issuer) {\n        return Err(anyhow::Error::new(OidcError::IssuerMismatch));\n    }\n    if let Some(ref aud) = token_data.claims.aud\n        && !aud_matches(aud, expected_client_id)\n    {\n        return Err(anyhow::Error::new(OidcError::AudienceMismatch));\n    }\n    if token_data.claims.nonce.as_deref() != Some(expected_nonce) {\n        return Err(anyhow::Error::new(OidcError::NonceMismatch));\n    }\n    Ok(OidcUserInfo {\n        user_id: token_data\n            .claims\n            .sub\n            .unwrap_or_else(|| \"unknown\".to_string()),\n        email: token_data.claims.email,\n        first_name: token_data.claims.first_name,\n        last_name: token_data.claims.last_name,\n        profile_image_asset_id: token_data.claims.picture,\n        principal_type: None,\n        principal_id: None,\n        team_id: None,\n        team_name: None,\n        team_role: None,\n        organization_id: None,\n        organization_name: None,\n        organization_role: None,","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs#L674-L710","documentation":"OidcError::NonceMismatch is thrown when the ID token's `nonce` claim differs from the expected_nonce supplied to validate_and_extract_user_info (protocol.rs:691-693). The nonce binds the ID token to a specific authorization request, preventing token replay/injection attacks; any mismatch means the token may not belong to this login attempt.","triggerScenarios":"extract_user_info receives a token whose `nonce` claim is missing or different from the nonce generated for the current authorization request.","commonSituations":"Replaying an old ID token from a previous login; the callback response mixed up between concurrent login sessions; nonce state overwritten or not persisted between the authorize redirect and the token exchange; a token endpoint response cached from an earlier attempt.","solutions":["Restart the login flow so a fresh nonce is generated and correctly carried through authorize -> callback -> token exchange.","Verify the nonce generated at authorize time is the same one passed as expected_nonce to extract_user_info (check state persistence).","Ensure concurrent logins store nonce state per-session, not in a single shared variable."],"exampleFix":"// before: shared nonce across concurrent sessions\nstatic NONCE: OnceLock<String> = OnceLock::new();\n// after: per-session nonce stored alongside the CSRF state\nsession_state.insert(sid, SessionState { nonce, pkce_verifier, .. });","handlingStrategy":"try-catch","validationCode":"// ensure nonce stored at authorize time is passed unchanged\nlet stored = session_state.get(&sid).map(|s| s.nonce.clone());\nassert_eq!(stored.as_deref(), Some(expected_nonce));","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"NonceMismatch\") => { /* restart login flow to mint a new nonce */ start_login_flow(); }\n    other => other,\n}","preventionTips":["Store nonce per-session alongside CSRF state, never globally.","Never reuse ID tokens across login attempts.","Persist nonce through redirects and clear it after successful validation."],"tags":["oidc","nonce","replay-protection","auth"],"backgroundTag":"jwt-nonce-mismatch","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}