{"record":{"id":"189df5e6aa80afde","repo":"spring-projects/spring-security","slug":"invalid-alg-jwk-parameter-in-jws-header-alg-j","errorCode":null,"errorMessage":"Invalid alg / jwk parameter in JWS Header: alg=, jwk.kty=","messagePattern":"Invalid alg / jwk parameter in JWS Header: alg=, jwk\\.kty=","errorType":"exception","errorClass":"BadJwtException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java","lineNumber":206,"sourceCode":"\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}\n\t\t\tcatch (JOSEException ex) {\n\t\t\t\tthrow new BadJwtException(\"Invalid jwk parameter in JWS Header.\");\n\t\t\t}\n\n\t\t\tthrow new BadJwtException(\"Invalid alg / jwk parameter in JWS Header: alg=\" + algorithm.getName()\n\t\t\t\t\t+ \", jwk.kty=\" + jwk.getKeyType().getValue());\n\t\t};\n\t}\n\n\tprivate static final class AthClaimValidator implements OAuth2TokenValidator<Jwt> {\n\n\t\tprivate final OAuth2Token accessToken;\n\n\t\tprivate AthClaimValidator(OAuth2Token accessToken) {\n\t\t\tAssert.notNull(accessToken, \"accessToken cannot be null\");\n\t\t\tthis.accessToken = accessToken;\n\t\t}\n\n\t\t@Override\n\t\tpublic OAuth2TokenValidatorResult validate(Jwt jwt) {\n\t\t\tAssert.notNull(jwt, \"DPoP proof jwt cannot be null\");\n\t\t\tString accessTokenHashClaim = jwt.getClaimAsString(\"ath\");\n\t\t\tif (!StringUtils.hasText(accessTokenHashClaim)) {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactory.java#L188-L224","documentation":"The DPoP proof's jwk key type does not match the JWS algorithm, so no selector branch applies. The message includes the alg header and the jwk's kty to diagnose the mismatch (e.g. alg=RS256 with kty=EC, or an unsupported kty like oct).","triggerScenarios":"In DPoPProofJwtDecoderFactory.jwsKeySelector, the embedded jwk parsed fine but either (a) algorithm is RSA-family while jwk is not RSAKey, (b) algorithm is EC-family while jwk is not ECKey, or (c) algorithm is outside both families; final fallback throw before 'Invalid alg / jwk parameter in JWS Header: alg=' + ... is reached.","commonSituations":"Client signs with RS256 but embeds an EC key (or vice versa); proofs using oct/symmetric keys or algorithms like HS256, which are not allowed for DPoP; typos in alg header when hand-building proofs.","solutions":["Make the signing algorithm consistent with the embedded key type: RSA key → RS256/PS256, EC key → ES256 (or matching curve).","Only embed an asymmetric public JWK matching the alg; symmetric (oct) keys and MAC algorithms are not valid for DPoP proofs.","If verifying, reject the proof with invalid_token and surface the alg/kty values from this message to help the client debug."],"exampleFix":"// before\nJWSSigner signer = new ECDSASigner(ecPrivateKey);\nsignedWith(algorithm = JWSAlgorithm.RS256, jwk = ecPublicJwk); // mismatch\n// after\nsignedWith(algorithm = JWSAlgorithm.ES256, jwk = ecPublicJwk);","handlingStrategy":"validation","validationCode":"JWSAlgorithm alg = jwsHeader.getAlgorithm();\nJWK jwk = jwsHeader.getJWK();\nboolean ok = (JWSAlgorithm.Family.RSA.contains(alg) && jwk instanceof RSAKey)\n    || (JWSAlgorithm.Family.EC.contains(alg) && jwk instanceof ECKey);\nif (!ok) { /* fix alg/kty pairing before sending */ }","typeGuard":"boolean algMatchesKey(JWSAlgorithm alg, JWK jwk) {\n    return (JWSAlgorithm.Family.RSA.contains(alg) && jwk instanceof RSAKey)\n        || (JWSAlgorithm.Family.EC.contains(alg) && jwk instanceof ECKey);\n}","tryCatchPattern":"try { decoder.decode(proof); } catch (BadJwtException e) { /* reject: alg/kty mismatch, e.getMessage() has details */ }","preventionTips":["Pick the signer from the key, not vice versa: RSA key → RS256/PS256, EC key → ES256","Never use MAC algorithms (HS256) or symmetric keys for DPoP proofs","Add a smoke test that signs and verifies a proof locally before deploying"],"tags":["jwk","dpop","algorithm-mismatch","jwt"],"backgroundTag":"invalid-argument-value","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"}