{"record":{"id":"2906f2d89a2d5351","repo":"spring-projects/spring-security","slug":"unsupported-algorithm-of-header-getalgorithm","errorCode":null,"errorMessage":"Unsupported algorithm of + header.getAlgorithm()","messagePattern":"Unsupported algorithm of \\+ header\\.getAlgorithm\\(\\)","errorType":"exception","errorClass":"BadJwtException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java","lineNumber":538,"sourceCode":"\t\t\t\t\tJWKSelector selector = createSelector(expectedJwsAlgorithms, jwt.getHeader());\n\t\t\t\t\treturn source.get(selector)\n\t\t\t\t\t\t.onErrorMap((ex) -> new IllegalStateException(\"Could not obtain the keys\", ex))\n\t\t\t\t\t\t.map((jwkList) -> createClaimsSet(processor, jwt, new JWKSecurityContext(jwkList)));\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\tprivate Function<JWSAlgorithm, Boolean> getExpectedJwsAlgorithms(JWSKeySelector<?> jwsKeySelector) {\n\t\t\tif (jwsKeySelector instanceof JWSVerificationKeySelector) {\n\t\t\t\treturn ((JWSVerificationKeySelector<?>) jwsKeySelector)::isAllowed;\n\t\t\t}\n\t\t\tthrow new IllegalArgumentException(\"Unsupported key selector type \" + jwsKeySelector.getClass());\n\t\t}\n\n\t\tprivate JWKSelector createSelector(Function<JWSAlgorithm, Boolean> expectedJwsAlgorithms, Header header) {\n\t\t\tJWSHeader jwsHeader = (JWSHeader) header;\n\t\t\tif (!expectedJwsAlgorithms.apply(jwsHeader.getAlgorithm())) {\n\t\t\t\tthrow new BadJwtException(\"Unsupported algorithm of \" + header.getAlgorithm());\n\t\t\t}\n\t\t\treturn new JWKSelector(JWKMatcher.forJWSHeader(jwsHeader));\n\t\t}\n\n\t}\n\n\t/**\n\t * A builder for creating {@link NimbusReactiveJwtDecoder} instances based on a public\n\t * key.\n\t *\n\t * @since 5.2\n\t */\n\tpublic static final class PublicKeyReactiveJwtDecoderBuilder {\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> JWT_TYPE_VERIFIER = new DefaultJOSEObjectTypeVerifier<>(\n\t\t\t\tJOSEObjectType.JWT, null);\n\n\t\tprivate static final JOSEObjectTypeVerifier<SecurityContext> NO_TYPE_VERIFIER = (header, context) -> {","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java#L520-L556","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder\n    .withJwkSetUri(jwksUri).build(); // defaults to RS256 only\n// after\nNimbusReactiveJwtDecoder decoder = NimbusReactiveJwtDecoder\n    .withJwkSetUri(jwksUri)\n    .jwsAlgorithms(algs -> { algs.add(SignatureAlgorithm.RS256); algs.add(SignatureAlgorithm.ES256); })\n    .build();","handlingStrategy":"try-catch","validationCode":"// client-side: check the token's alg before sending\nString[] parts = jwt.split(\"\\\\.\");\nString header = new String(Base64.getUrlDecoder().decode(parts[0]));\nif (!header.contains(\"\\\"RS256\\\"\")) { /* reconfigure decoder or reject token */ }","typeGuard":"boolean hasSupportedAlg(String jwt, Set<S signatureAlgorithm> supported) {\n    String header = new String(Base64.getUrlDecoder().decode(jwt.split(\"\\\\.\")[0]));\n    return supported.stream().anyMatch(a -> header.contains(a.getName()));\n}","tryCatchPattern":"try {\n    Jwt jwt = decoder.decode(token);\n} catch (BadJwtException e) {\n    log.warn(\"Rejected JWT: {}\", e.getMessage()); // algorithm/structure rejected\n    throw new UnauthorizedException(\"Token algorithm not accepted\");\n}","preventionTips":["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"],"tags":["jwt","oauth2","algorithm-validation","reactive"],"backgroundTag":"jwt-verification-failed","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}