xai-org/grok-build · error · OidcError

OidcError::StateMismatch

Error message

OidcError::StateMismatch

What it means

OidcError::StateMismatch is raised by validate_state when the `state` parameter received on the OIDC callback does not equal the state value generated at login start. State is a CSRF protection; a mismatch means the callback response cannot be trusted to belong to this login session.

Source

Thrown at crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs:593

    pub(super) first_name: Option<String>,
    #[serde(default, alias = "family_name")]
    pub(super) last_name: Option<String>,
    #[serde(default)]
    pub(super) picture: Option<String>,
}
pub(super) fn aud_matches(aud: &serde_json::Value, expected: &str) -> bool {
    match aud {
        serde_json::Value::String(s) => s == expected,
        serde_json::Value::Array(values) => values
            .iter()
            .any(|v| matches!(v, serde_json::Value::String(s) if s == expected)),
        _ => false,
    }
}
pub(super) fn validate_state(expected: &str, received: &str) -> anyhow::Result<()> {
    if received != expected {
        tracing::warn!(expected = %expected, received = %received, "OIDC: state mismatch");
        return Err(anyhow::Error::new(OidcError::StateMismatch));
    }
    Ok(())
}
/// Explicit JWA name mapping — avoids coupling to `jsonwebtoken::Algorithm`'s `Debug` repr.
pub(super) fn alg_to_jwa_name(alg: jsonwebtoken::Algorithm) -> &'static str {
    match alg {
        jsonwebtoken::Algorithm::RS256 => "RS256",
        jsonwebtoken::Algorithm::RS384 => "RS384",
        jsonwebtoken::Algorithm::RS512 => "RS512",
        jsonwebtoken::Algorithm::PS256 => "PS256",
        jsonwebtoken::Algorithm::PS384 => "PS384",
        jsonwebtoken::Algorithm::PS512 => "PS512",
        jsonwebtoken::Algorithm::ES256 => "ES256",
        jsonwebtoken::Algorithm::ES384 => "ES384",
        jsonwebtoken::Algorithm::EdDSA => "EdDSA",
        other => match other {
            jsonwebtoken::Algorithm::HS256 => "HS256",
            jsonwebtoken::Algorithm::HS384 => "HS384",

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Restart the login flow cleanly and use only the most recent login session's URL
  2. Ensure no other login is running in parallel that could mix callbacks
  3. Do not reuse or hand-edit the authorize URL; let the flow generate a fresh state
Defensive patterns

Strategy: validation

Validate before calling

// client-side sanity: ensure the callback state matches what the flow generated
fn states_match(expected: &str, received: &str) -> bool {
    !expected.is_empty() && constant_time_eq(expected.as_bytes(), received.as_bytes())
}

Try / catch

match res {
    Err(e) if matches!(e.downcast_ref::<OidcError>(), Some(OidcError::StateMismatch)) => {
        eprintln!("CSRF state mismatch — restart the login and use the newest URL");
    }
    other => other?,
}

Prevention

When it happens

Trigger: run_login_flow_with_config receives a callback and calls validate_state(expected, received); any string inequality triggers StateMismatch.

Common situations: Multiple concurrent login sessions whose callbacks crossed, browser/race delivering a stale callback, replayed or forged redirect, truncation of the state in a URL-length-limited relay.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/180e7a2a6427a9a2. Report an issue: GitHub.