xai-org/grok-build · error · OidcError

OidcError::JwkNotFound

Error message

OidcError::JwkNotFound

What it means

OidcError::JwkNotFound is thrown in validate_and_extract_user_info when the `kid` header of the received ID token does not match any key in the JSON Web Key Set (JWKS) fetched from the provider's `jwks_uri`. The library cannot obtain the public key needed to verify the token's signature, so it refuses to trust the token. This guards against forged or from-an-unknown-IdP tokens.

Source

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

        .ok_or_else(|| anyhow::Error::new(OidcError::IdTokenMissingKid))?;
    let jwks_uri = discovery
        .jwks_uri
        .as_ref()
        .ok_or_else(|| anyhow::Error::new(OidcError::DiscoveryMissingJwksUri))?;
    let jwks: jsonwebtoken::jwk::JwkSet = with_alpha_test_key(
        crate::http::shared_client()
            .get(jwks_uri)
            .timeout(std::time::Duration::from_secs(10)),
        jwks_uri,
    )
    .send()
    .await?
    .error_for_status()?
    .json()
    .await?;
    let jwk = jwks
        .find(&kid)
        .ok_or_else(|| anyhow::Error::new(OidcError::JwkNotFound { kid: kid.clone() }))?;
    let decoding_key = jsonwebtoken::DecodingKey::from_jwk(jwk)?;
    let alg = header.alg;
    ensure_alg_allowed(
        alg,
        discovery.id_token_signing_alg_values_supported.as_deref(),
    )?;
    let mut validation = jsonwebtoken::Validation::new(alg);
    validation.set_issuer(&[expected_issuer]);
    validation.set_audience(&[expected_client_id]);
    validation.validate_exp = true;
    validation.validate_aud = true;
    validation.required_spec_claims = ["sub", "iss", "aud", "exp"]
        .into_iter()
        .map(ToOwned::to_owned)
        .collect();
    let token_data = jsonwebtoken::decode::<IdTokenClaims>(token, &decoding_key, &validation)?;
    if token_data.claims.iss.as_deref() != Some(expected_issuer) {
        return Err(anyhow::Error::new(OidcError::IssuerMismatch));

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Re-run the OIDC login flow to obtain a freshly signed ID token from the current provider keys.
  2. Verify discovery (issuer/jwks_uri) points at the same environment that issued the token.
  3. Check the provider's JWKS endpoint manually and confirm the token's `kid` is present (curl the jwks_uri).
  4. If the IdP recently rotated keys, wait for key publication/propagation and retry.

Example fix

// before: validating a token signed by another environment
let discovery = fetch_discovery("https://auth.example.com").await?;
let user = extract_user_info(Some(&staging_token), &discovery, ...).await?;
// after: use a token from the same environment, or refresh via login
let discovery = fetch_discovery("https://staging.auth.example.com").await?;
let user = extract_user_info(Some(&staging_token), &discovery, ...).await?;
Defensive patterns

Strategy: retry

Validate before calling

// before calling: check the token kid exists in the JWKS
let header = jsonwebtoken::decode_header(token)?;
let jwks: JwkSet = reqwest::get(&jwks_uri).await?.json().await?;
if let Some(kid) = header.kid {
    if jwks.find(&kid).is_none() { eprintln!("kid {kid} not in JWKS; refresh token/login"); }
}

Type guard

fn jwk_available(jwks: &JwkSet, kid: &str) -> bool { jwks.find(kid).is_some() }

Try / catch

match extract_user_info(...).await {
    Err(e) if e.to_string().contains("JwkNotFound") => retry_login_with_fresh_token(),
    other => other,
}

Prevention

When it happens

Trigger: Calling extract_user_info/validate_and_extract_user_info with an ID token whose `kid` is absent from the JWKS currently published at discovery.jwks_uri (lines 664-666 of protocol.rs).

Common situations: The identity provider rotated signing keys and the old key was removed before the cached token was validated; the token was issued by a different environment/tenant (staging token against prod discovery); a misconfigured issuer URL points the client at the wrong provider's JWKS; token tampering.

Related errors


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