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
- Add the token's algorithm to the decoder: .jwsAlgorithms(algs -> algs.add(SignatureAlgorithm.ES256)) on NimbusReactiveJwtDecoder.withJwkSetUri(...).build()
- Decode the JWT header (jwt.io or header of the base64url token) to confirm which 'alg' is actually being issued
- Update the Authorization Server to emit an algorithm the resource server accepts
- 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
- Always configure jwsAlgorithms explicitly instead of relying on defaults
- Track Authorization Server algorithm rotation and update decoder config in lockstep
- Add an integration test decoding a real token from each environment
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
- Unsupported algorithm of + jwt.getHeader().getAlgorithm()
- missing_signature_verifier
- An error occurred while attempting to decode the Jwt: +…
- Failed to encode the JWT due to signing error: Failed to…
- Failed to validate the token
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)