{"record":{"id":"6931436ab2c69b8e","repo":"spring-projects/spring-security","slug":"unsupported-alg-parameter-in-jws-header-algorit","errorCode":null,"errorMessage":"Unsupported alg parameter in JWS Header: ${algorithm.getName()}","messagePattern":"Unsupported alg parameter in JWS Header: (.+?)","errorType":"exception","errorClass":"BadJwtException","httpStatus":400,"severity":"error","filePath":"oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java","lineNumber":183,"sourceCode":"\t\treturn delegatingTokenValidator;\n\t}\n\n\tprivate static NimbusJwtDecoder buildDecoder() {\n\t\tConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();\n\t\tjwtProcessor.setJWSTypeVerifier(DPOP_TYPE_VERIFIER);\n\t\tjwtProcessor.setJWSKeySelector(jwsKeySelector());\n\t\t// Override the default Nimbus claims set verifier and use jwtValidatorFactory for\n\t\t// claims validation\n\t\tjwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {\n\t\t});\n\t\treturn new NimbusJwtDecoder(jwtProcessor);\n\t}\n\n\tprivate static JWSKeySelector<SecurityContext> jwsKeySelector() {\n\t\treturn (header, context) -> {\n\t\t\tJWSAlgorithm algorithm = header.getAlgorithm();\n\t\t\tif (!JWSAlgorithm.Family.RSA.contains(algorithm) && !JWSAlgorithm.Family.EC.contains(algorithm)) {\n\t\t\t\tthrow new BadJwtException(\"Unsupported alg parameter in JWS Header: \" + algorithm.getName());\n\t\t\t}\n\n\t\t\tJWK jwk = header.getJWK();\n\t\t\tif (jwk == null) {\n\t\t\t\tthrow new BadJwtException(\"Missing jwk parameter in JWS Header.\");\n\t\t\t}\n\t\t\tif (jwk.isPrivate()) {\n\t\t\t\tthrow new BadJwtException(\"Invalid jwk parameter in JWS Header.\");\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (JWSAlgorithm.Family.RSA.contains(algorithm) && jwk instanceof RSAKey rsaKey) {\n\t\t\t\t\treturn Collections.singletonList(rsaKey.toRSAPublicKey());\n\t\t\t\t}\n\t\t\t\telse if (JWSAlgorithm.Family.EC.contains(algorithm) && jwk instanceof ECKey ecKey) {\n\t\t\t\t\treturn Collections.singletonList(ecKey.toECPublicKey());\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java#L165-L201","documentation":"This BadJwtException is thrown by DPoPProofJwtDecoderFactory's jwsKeySelector when the DPoP proof JWT's JWS header declares an algorithm outside the RSA or EC families (e.g. HS256 or none). The factory only accepts asymmetric RSA/EC algorithms, per RFC 9449, and rejects everything else before key selection.","triggerScenarios":"Decoding a DPoP proof JWT whose JOSE header alg is not in JWSAlgorithm.Family.RSA or Family.EC (e.g. HS256, PS-misconfigured variants are fine, but HS256/others throw).","commonSituations":"A client signs the proof with HS256 (symmetric), uses 'none', or an older client library defaults to a non-RSA/EC algorithm; or a malicious/misrouted JWT is fed to the DPoP decoder.","solutions":["Use RS256/RS384/RS512, ES256/ES384/ES512, or PS* algorithms when creating the DPoP proof (e.g. RSA JWS signer or ECDSASigner in Nimbus).","Catch BadJwtException on decode and return a 401 invalid_dpop_proof error.","If you control the client config, ensure its DPoP signer matches RSA or EC keys.","Verify the proof header is produced by a DPoP library, not hand-rolled JWT code defaulting to HS256."],"exampleFix":"// before\nJWSSigner signer = new MACSigner(secret); // produces HS256 -> rejected\n// after\nJWSSigner signer = new RSASSASigner(rsaKey); // RS256, accepted","handlingStrategy":"validation","validationCode":"Set<JWSAlgorithm> allowed = new HashSet<>();\nallowed.addAll(JWSAlgorithm.Family.RSA);\nallowed.addAll(JWSAlgorithm.Family.EC);\nif (!allowed.contains(header.getAlgorithm())) { throw new IllegalArgumentException(\"alg not allowed\"); }","typeGuard":"boolean isRsaOrEc(JWSAlgorithm alg) {\n    return JWSAlgorithm.Family.RSA.contains(alg) || JWSAlgorithm.Family.EC.contains(alg);\n}","tryCatchPattern":"try {\n    Jwt jwt = decoder.decode(proof);\n} catch (BadJwtException ex) {\n    throw new OAuth2AuthorizationCodeException(...); // respond 401 invalid_dpop_proof\n}","preventionTips":["Sign DPoP proofs with RSA or EC keys only — never HMAC (HS256).","Add a header-alg assertion in your client's proof-generation tests.","Reject 'none' and symmetric algs at the client boundary."],"tags":["dpop","jwt","jws","oauth2","spring-security"],"backgroundTag":"unsupported-jws-algorithm","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}