spring-projects/spring-security · error · BadJwtException

Unsupported algorithm of + header.getAlgorithm()

Error message

Unsupported algorithm of + header.getAlgorithm()

What it means

NimbusReactiveJwtDecoder validates that the JWT's JWS algorithm (from the JOSE header) is one of the algorithms the decoder was explicitly configured to accept. If the incoming signed JWT uses an algorithm outside that configured allow-list, a BadJwtException with 'Unsupported algorithm of <alg>' is thrown by the JWK selector. This prevents algorithm-confusion attacks and enforces the deployment's key/algorithm policy.

Solutions

  1. Add the token's algorithm to the decoder: .jwsAlgorithms(algs -> algs.add(SignatureAlgorithm.ES256)) on NimbusReactiveJwtDecoder.withJwkSetUri(...).build()
  2. Decode the JWT header (jwt.io or header of the base64url token) to confirm which 'alg' is actually being issued
  3. Update the Authorization Server to emit an algorithm the resource server accepts
  4. If HMAC tokens must be supported, use NimbusReactiveJwtDecoder.withSecretKey(...) instead of a JWK-set-based decoder

Example fix

// before
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder
    .withJwkSetUri(jwksUri).build(); // defaults to RS256 only
// after
NimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder
    .withJwkSetUri(jwksUri)
    .jwsAlgorithms(algs -> { algs.add(SignatureAlgorithm.RS256); algs.add(SignatureAlgorithm.ES256); })
    .build();
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: check the token's alg before sending
String[] parts = jwt.split("\\.");
String header = new String(Base64.getUrlDecoder().decode(parts[0]));
if (!header.contains("\"RS256\"")) { /* reconfigure decoder or reject token */ }

Type guard

boolean hasSupportedAlg(String jwt, Set<S signatureAlgorithm> supported) {
    String header = new String(Base64.getUrlDecoder().decode(jwt.split("\\.")[0]));
    return supported.stream().anyMatch(a -> header.contains(a.getName()));
}

Try / catch

try {
    Jwt jwt = decoder.decode(token);
} catch (BadJwtException e) {
    log.warn("Rejected JWT: {}", e.getMessage()); // algorithm/structure rejected
    throw new UnauthorizedException("Token algorithm not accepted");
}

Prevention

When it happens

Trigger: A JWT signed with e.g. HS256 arrives at a decoder built with jwkSetUri()/jwkSource() and .jwsAlgorithm(SignatureAlgorithm.RS256) (or the default RS256-only configuration); or the Authorization Server rotates to a new algorithm (ES256/PS256) before the resource server config is updated.

Common situations: Migrating an identity provider from RS256 to ES256 or PS256; configuring multiple decoders and sending a token to the wrong one; copy-pasting a decoder config from a tutorial that hardcodes RS256; symmetric (HMAC) tokens hitting an asymmetric-key decoder.

Related errors


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

Appendix: source

Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java:538

					JWKSelector selector = createSelector(expectedJwsAlgorithms, jwt.getHeader());
					return source.get(selector)
						.onErrorMap((ex) -> new IllegalStateException("Could not obtain the keys", ex))
						.map((jwkList) -> createClaimsSet(processor, jwt, new JWKSecurityContext(jwkList)));
				});
			};
		}

		private Function<JWSAlgorithm, Boolean> getExpectedJwsAlgorithms(JWSKeySelector<?> jwsKeySelector) {
			if (jwsKeySelector instanceof JWSVerificationKeySelector) {
				return ((JWSVerificationKeySelector<?>) jwsKeySelector)::isAllowed;
			}
			throw new IllegalArgumentException("Unsupported key selector type " + jwsKeySelector.getClass());
		}

		private JWKSelector createSelector(Function<JWSAlgorithm, Boolean> expectedJwsAlgorithms, Header header) {
			JWSHeader jwsHeader = (JWSHeader) header;
			if (!expectedJwsAlgorithms.apply(jwsHeader.getAlgorithm())) {
				throw new BadJwtException("Unsupported algorithm of " + header.getAlgorithm());
			}
			return new JWKSelector(JWKMatcher.forJWSHeader(jwsHeader));
		}

	}

	/**
	 * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a public
	 * key.
	 *
	 * @since 5.2
	 */
	public static final class PublicKeyReactiveJwtDecoderBuilder {

		private static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(
				JOSEObjectType.JWT, null);

		private static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {

View on GitHub (pinned to 96852e8860)