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
- Re-run the OIDC login flow to obtain a freshly signed ID token from the current provider keys.
- Verify discovery (issuer/jwks_uri) points at the same environment that issued the token.
- Check the provider's JWKS endpoint manually and confirm the token's `kid` is present (curl the jwks_uri).
- 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
- Always obtain tokens via a fresh login against the same environment as the discovery document.
- Cache JWKS with a TTL and refetch on unknown kid.
- Monitor IdP key-rotation announcements.
- Verify issuer/jwks_uri configuration per environment.
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
- no OIDC auth entry found in {}. Run `grok login` first.
- auth entry has no refresh_token — cannot refresh expired tok
- auth entry has no oidc_issuer — cannot refresh expired token
- auth entry has no oidc_client_id — cannot refresh expired to
- OidcError::IdTokenMissingKid
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/1e7567736ec35add.
Report an issue: GitHub.