spring-projects/spring-security · error · OAuth2AuthorizationException
invalid_key
invalid_key
Error message
Failed to resolve JWK signing key for client registration '${registrationId}'. What it means
For clients that authenticate with a signed JWT (private_key_jwt / client_secret_jwt), NimbusJwtClientAuthenticationParametersConverter asks the configured jwkResolver for a JWK. When the resolver returns null, Spring Security cannot sign the client assertion and throws invalid_key. This means no signing key was found for the given client registration.
Source
Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverter.java:124
@Override
public @Nullable MultiValueMap<String, String> convert(T authorizationGrantRequest) {
Assert.notNull(authorizationGrantRequest, "authorizationGrantRequest cannot be null");
ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration();
if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.equals(clientRegistration.getClientAuthenticationMethod())
&& !ClientAuthenticationMethod.CLIENT_SECRET_JWT
.equals(clientRegistration.getClientAuthenticationMethod())) {
return null;
}
JWK jwk = this.jwkResolver.apply(clientRegistration);
if (jwk == null) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_KEY_ERROR_CODE,
"Failed to resolve JWK signing key for client registration '"
+ clientRegistration.getRegistrationId() + "'.",
null);
throw new OAuth2AuthorizationException(oauth2Error);
}
JwsAlgorithm jwsAlgorithm = resolveAlgorithm(jwk);
if (jwsAlgorithm == null) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_ALGORITHM_ERROR_CODE,
"Unable to resolve JWS (signing) algorithm from JWK associated to client registration '"
+ clientRegistration.getRegistrationId() + "'.",
null);
throw new OAuth2AuthorizationException(oauth2Error);
}
JwsHeader.Builder headersBuilder = JwsHeader.with(jwsAlgorithm);
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(Duration.ofSeconds(60));
// @formatter:off
JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder()View on GitHub (pinned to 96852e8860)
Solutions
- Ensure a JWK resolver is configured: NimbusJwtClientAuthenticationParametersConverter<JwkResolvers...> with a resolver returning a JWK for the registrationId.
- Verify the keystore/JWK Set actually contains a key matching the registrationId and type (RSA for RS256/PS256, EC for ES256).
- Check the resolver's filter logic (algorithm/key-use constraints) isn't discarding the only available key.
- Confirm the registration actually requires client authentication via JWT; if not, use client_secret_basic instead.
Example fix
// before: resolver returns null for unknown registrations JWK jwk = jwkSet.getKeys().stream().filter(k -> matches(k)).findFirst().orElse(null); // after: fail fast at startup if the key is absent JWK jwk = Objects.requireNonNull(resolveJwk(registrationId), "No JWK for " + registrationId);
Defensive patterns
Strategy: validation
Validate before calling
// startup check
JWK jwk = jwkResolver.apply(clientRegistration);
if (jwk == null) {
throw new IllegalStateException(
"No signing JWK available for registration " + clientRegistration.getRegistrationId()
+ "; check keystore/JWK Set and resolver filter");
} Type guard
boolean hasSigningJwk(ClientRegistration reg) {
JWK jwk = jwkResolver.apply(reg);
return jwk != null && "sig".equals(jwk.getKeyUse() != null ? jwk.getKeyUse().getValue() : null);
} Try / catch
catch (OAuth2AuthorizationException ex) { if ("invalid_key".equals(ex.getError().getErrorCode())) { log.error("Missing signing JWK for registration; failing fast"); throw new ConfigurationException(ex); } throw ex; } Prevention
- Fail at startup if the keystore/JWK Set has no key for each private_key_jwt registration.
- Name/lookup JWKs by registrationId consistently and test the resolver in unit tests.
- Keep kty/use/alg metadata populated on exported JWKs.
- Mount/decrypt keystores before the application accepts traffic.
When it happens
Trigger: Thrown in convert() when this.jwkResolver.apply(clientRegistration) returns null — typically because the JWKSet/source registered for that registrationId has no key, or the resolver's predicate filters it out.
Common situations: Keystore or JWK Set source not loaded/empty, wrong registrationId mapped in the resolver, key type mismatch (resolver expects RSA but keystore holds EC), or developer forgot to configure a jwkResolver at all while using private_key_jwt.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/2514e1c04c1db7fe.
Report an issue: GitHub.