spring-projects/spring-security · error · JwtEncodingException

Failed to select a key since there are multiple for the…

Error message

Failed to select a key since there are multiple for the signing algorithm [%s]; please specify a selector in NimbusJwsEncoder#setJwkSelector

What it means

NimbusJwtEncoder's default jwkSelector cannot decide which key to use when the JWKSource returns more than one JWK matching the signing algorithm/headers. Instead of guessing, it throws JwtEncodingException telling the developer to supply an explicit selector via setJwkSelector. This is a deliberate guard against silently signing with the wrong key.

Solutions

  1. Call encoder.setJwkSelector(jwks -> jwks.get(0)) if any matching key is acceptable, or better, a selector that filters by kid/key use.
  2. Prefer narrowing the JWKSource itself (e.g. JWKSet with a single active signing key) so only one key matches.
  3. Add a distinct "kid" per key and implement a selector that picks the JWK whose.getKeyID() equals the intended kid.
  4. If keys differ by use, set "use":"sig" on signing keys so non-signing keys no longer match the JWKSelector matcher.
  5. For rotation, keep exactly one active key in the source used for signing while exposing old keys only for verification.

Example fix

// before
NimbusJwtEncoder encoder = new NimbusJwtEncoder(jwkSource); // multiple matching keys -> throws
// after
encoder.setJwkSelector((jwks) -> jwks.stream()
	.filter(jwk -> "my-key-id".equals(jwk.getKeyID()))
	.findFirst()
	.orElseThrow(() -> new JwtEncodingException("no matching kid")));
Defensive patterns

Strategy: validation

Validate before calling

// Ensure exactly one key will match before encoding, or install a deterministic selector
List<JWK> candidates = jwkSource.get(new JWKSelector(
	new JWKMatcher.Builder().algorithm(JWSAlgorithm.RS256.getName()).build()), null);
if (candidates == null || candidates.size() > 1) {
	encoder.setJwkSelector(jwks -> jwks.stream()
		.filter(j -> "my-kid".equals(j.getKeyID()))
		.findFirst()
		.orElseThrow());
}

Try / catch

try {
	Jwt jwt = encoder.encode(params);
} catch (org.springframework.security.oauth2.jwt.JwtEncodingException ex) {
	if (ex.getMessage().contains("multiple")) {
		// fall back to a kid-based selector configured at startup
	}
	throw ex;
}

Prevention

When it happens

Trigger: Calling NimbusJwtEncoder.encode(JwtEncoderParameters) (or via JwtGenerator) when the configured jwkSource (e.g. an ImmutableJWKSet holding a JWKSet with several keys of the same algorithm/type, or a rotating key set) matches multiple keys for the request's JWS headers, and setJwkSelector was never called with a custom Converter<List<JWK>, JWK>.

Common situations: Key rotation deployments where the JWKS contains old + new keys for the same algorithm; a JWKSet containing both RSA and EC (or multiple RSA) keys that all match the matcher; copying multi-key examples without narrowing by kid; upgrading Spring Security to a version that defaults to throwing instead of picking the first key.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/86d07ce6d05b05b2. Report an issue: GitHub.

Appendix: source

Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoder.java:115

 * @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus
 * JOSE + JWT SDK</a>
 */
public final class NimbusJwtEncoder implements JwtEncoder {

	private static final String ENCODING_ERROR_MESSAGE_TEMPLATE = "An error occurred while attempting to encode the Jwt: %s";

	private static final JwsHeader DEFAULT_JWS_HEADER = JwsHeader.with(SignatureAlgorithm.RS256).build();

	private static final JWSSignerFactory JWS_SIGNER_FACTORY = new DefaultJWSSignerFactory();

	private final JwsHeader defaultJwsHeader;

	private final Map<JWK, JWSSigner> jwsSigners = new ConcurrentHashMap<>();

	private final JWKSource<SecurityContext> jwkSource;

	private Converter<List<JWK>, JWK> jwkSelector = (jwks) -> {
		throw new JwtEncodingException(
				String.format(
						"Failed to select a key since there are multiple for the signing algorithm [%s]; "
								+ "please specify a selector in NimbusJwsEncoder#setJwkSelector",
						jwks.get(0).getAlgorithm()));
	};

	/**
	 * Constructs a {@code NimbusJwtEncoder} using the provided parameters.
	 * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}
	 */
	public NimbusJwtEncoder(JWKSource<SecurityContext> jwkSource) {
		this.defaultJwsHeader = DEFAULT_JWS_HEADER;
		Assert.notNull(jwkSource, "jwkSource cannot be null");
		this.jwkSource = jwkSource;
	}

	private NimbusJwtEncoder(JWK jwk) {
		Assert.notNull(jwk, "jwk cannot be null");

View on GitHub (pinned to 96852e8860)