{"record":{"id":"3ea3571ee8b15785","repo":"xai-org/grok-build","slug":"oidcerror-idtokenmissingkid","errorCode":null,"errorMessage":"OidcError::IdTokenMissingKid","messagePattern":"OidcError::IdTokenMissingKid","errorType":"validation","errorClass":"OidcError","httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs","lineNumber":648,"sourceCode":"        return Err(anyhow::Error::new(\n            OidcError::AlgNotInDiscoverySupportedList {\n                alg: alg_name.to_owned(),\n            },\n        ));\n    }\n    Ok(())\n}\npub(super) async fn validate_and_extract_user_info(\n    token: &str,\n    discovery: &Discovery,\n    expected_issuer: &str,\n    expected_client_id: &str,\n    expected_nonce: &str,\n) -> anyhow::Result<OidcUserInfo> {\n    let header = jsonwebtoken::decode_header(token)?;\n    let kid = header\n        .kid\n        .ok_or_else(|| anyhow::Error::new(OidcError::IdTokenMissingKid))?;\n    let jwks_uri = discovery\n        .jwks_uri\n        .as_ref()\n        .ok_or_else(|| anyhow::Error::new(OidcError::DiscoveryMissingJwksUri))?;\n    let jwks: jsonwebtoken::jwk::JwkSet = with_alpha_test_key(\n        crate::http::shared_client()\n            .get(jwks_uri)\n            .timeout(std::time::Duration::from_secs(10)),\n        jwks_uri,\n    )\n    .send()\n    .await?\n    .error_for_status()?\n    .json()\n    .await?;\n    let jwk = jwks\n        .find(&kid)\n        .ok_or_else(|| anyhow::Error::new(OidcError::JwkNotFound { kid: kid.clone() }))?;","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/auth/oidc/protocol.rs#L630-L666","documentation":"OidcError::IdTokenMissingKid is raised by validate_and_extract_user_info when the id_token's JOSE header lacks a `kid` (key ID). The library needs `kid` to select the matching JWK from the provider's JWKS for signature verification, so a token without it cannot be validated.","triggerScenarios":"decode_header(token) succeeds but header.kid is None during id_token validation after the token exchange/refresh.","commonSituations":"Providers that sign with a single static key and omit `kid` in the JWS header; non-standard or homegrown OIDC servers; proxy-rewritten tokens.","solutions":["Configure the IdP to include the kid header in issued id_tokens (standard behavior for multi-key JWKS providers)","If the provider uses a single key, it must still set kid to conform to OIDC spec; file/request a provider fix","Try a different provider/realm that issues spec-compliant tokens"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// inspect the unverified header first; fail fast with a clearer message\nfn header_has_kid(token: &str) -> bool {\n    jsonwebtoken::decode_header(token).map(|h| h.kid.is_some()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match res {\n    Err(e) if matches!(e.downcast_ref::<OidcError>(), Some(OidcError::IdTokenMissingKid)) => {\n        eprintln!(\"Provider omits kid in id_token header; provider must include kid per OIDC spec\");\n    }\n    other => other?,\n}","preventionTips":["Verify with jwt.io / decode_header that issued tokens carry a kid header","Prefer mainstream IdPs (Keycloak, Auth0, Okta, Entra) that always set kid","Reject non-compliant providers during onboarding rather than at login time"],"tags":["oidc","jwt","jwks"],"backgroundTag":"jwt-missing-kid-header","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}