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

  1. Ensure a JWK resolver is configured: NimbusJwtClientAuthenticationParametersConverter<JwkResolvers...> with a resolver returning a JWK for the registrationId.
  2. Verify the keystore/JWK Set actually contains a key matching the registrationId and type (RSA for RS256/PS256, EC for ES256).
  3. Check the resolver's filter logic (algorithm/key-use constraints) isn't discarding the only available key.
  4. 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

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.