quarkusio/quarkus · error · org.jose4j.keys.resolvers.UnresolvableKeyException
JWK with kid '%s' is not available
Error message
JWK with kid '%s' is not available
What it means
Thrown by OidcProvider's JsonWebKeyResolver.resolveKey when a signed JWT carries a 'kid' (key ID) header but no matching JWK can be found in the tenant's JSON Web Key Set. Per RFC 7515 semantics, once the token declares a 'kid' the verifier must use exactly that key; the provider refuses to fall back to other keys or thumbprint matching. This is a fail-fast safeguard against tokens signed with unknown or rotated-out keys.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java:554
if (oidcConfig.certificateChain().trustStoreFile().isPresent()) {
chainResolverFallback = new CertChainPublicKeyResolver(oidcConfig);
} else {
chainResolverFallback = null;
}
}
@Override
public Key resolveKey(JsonWebSignature jws, List<JsonWebStructure> nestingContext)
throws UnresolvableKeyException {
Key key = null;
// Try 'kid' first
String kid = jws.getKeyIdHeaderValue();
if (kid != null) {
key = getKeyWithId(kid);
if (key == null) {
// if `kid` was set then the key must exist
throw new UnresolvableKeyException(String.format("JWK with kid '%s' is not available", kid));
}
}
String thumbprint = null;
if (key == null) {
thumbprint = jws.getHeader(HeaderParameterNames.X509_CERTIFICATE_SHA256_THUMBPRINT);
if (thumbprint != null) {
key = getKeyWithS256Thumbprint(thumbprint);
if (key == null) {
// if only `x5tS256` was set then the key must exist
throw new UnresolvableKeyException(
String.format("JWK with the SHA256 certificate thumbprint '%s' is not available", thumbprint));
}
}
}
if (key == null) {
thumbprint = jws.getHeader(HeaderParameterNames.X509_CERTIFICATE_THUMBPRINT);View on GitHub (pinned to e1c734241f)
Solutions
- Force a JWKS refresh (restart the app or invalidate the cached key set) so keys rotated at the provider are re-fetched
- Verify the token was issued by the tenant/realm you configured — check quarkus.oidc.auth-server-url / jwks-path matches the token issuer
- Compare the token's 'kid' header (decode the JWT at jwt.io or similar) against the keys published at the JWKS endpoint
- If the provider doesn't publish 'kid' but the token has one, remove the mismatch or configure the signing key locally via quarkus.oidc.token.public-key / certificate
Example fix
// token kid 'key-2' but only 'key-1' published after rotation // before: stale cached JWKS -> UnresolvableKeyException // after: make refresh interval aggressive and align tenant config quarkus.oidc.token.audience=...
Defensive patterns
Strategy: fallback
Validate before calling
// decode token header and check kid is present in fetched JWKS before verification
String kid = com.nimbusds.jose.JOSEHeader parse of token; // or decode base64url header JSON
Set<String> availableKids = fetchedJwks.getKeys().stream().map(JsonWebKey::getKeyId).collect(Collectors.toSet());
if (!availableKids.contains(kid)) { throw new IllegalStateException("kid " + kid + " missing from JWKS"); } Type guard
boolean jwksHasKid(String jwksJson, String kid) {
var set = io.quarkus.oidc.runtime.OidcUtils.parse JWKS if available;
return set != null && set.getKeys().stream().anyMatch(k -> kid.equals(k.getKeyId()));
} Try / catch
try {
SecurityIdentity id = identityProvider.authenticate(...);
} catch (AuthenticationFailedException e) {
if (e.getCause() instanceof UnresolvableKeyException) {
// request JWKS refresh / retry verification once
}
} Prevention
- Set a short JWKS refresh interval so provider key rotation is picked up quickly
- Monitor the token's kid header against the JWKS endpoint in CI against your providers
- Pin signing keys locally for trusted providers to avoid runtime resolution
- Keep tenant auth-server-url/jwks-path in sync with the actual token issuer
When it happens
Trigger: Verifying an ID token/access token whose JOSE header contains 'kid' while getKeyWithId(kid) returns null — i.e. the JWKS fetched from the OIDC provider (or the local quarkus.oidc.* jwks/key config) has no key with that 'kid'.
Common situations: The OIDC provider rotated signing keys but the app still holds a cached/stale JWKS; a typo'd or misconfigured jwks.path / jwks.resolve-early setting; testing tokens issued by a different realm/tenant than the one configured; custom or third-party identity providers whose 'kid' values don't match what's published in the JWKS endpoint.
Related errors
- JWK is not available, neither 'kid' nor 'x5t#S256' nor 'x5t'
- %s type can not be used to represent JWT claims in @Singleto
- DPoP proof token signature is invalid
- Opaque access token can not be converted to JsonWebToken
- ISSUED_AT_INVALID_PAST
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/a06d640d8088074b.
Report an issue: GitHub.