quarkusio/quarkus · error · AuthenticationFailedException
DPoP proof JWK key is a private key but it must be a public
Error message
DPoP proof JWK key is a private key but it must be a public key
What it means
Thrown when the JWK embedded in the DPoP proof header contains (or resolves to) private key material. Per RFC 9449 the proof must include only the public key; shipping a private key would leak secret material and is rejected outright.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java:280
JsonObject proofHeaders = (JsonObject) requestData.get(OidcUtils.DPOP_PROOF_JWT_HEADERS);
JsonObject jwkProof = proofHeaders.getJsonObject(OidcConstants.DPOP_JWK_HEADER);
if (jwkProof == null) {
LOG.warn("DPoP proof jwk header is missing");
throw new AuthenticationFailedException(invalidDPoPProofMap(request.getToken()));
}
PublicJsonWebKey publicJsonWebKey = null;
try {
publicJsonWebKey = PublicJsonWebKey.Factory.newPublicJwk(jwkProof.getMap());
} catch (JoseException ex) {
LOG.warn("DPoP proof jwk header does not represent a valid JWK key");
throw new AuthenticationFailedException(ex, invalidDPoPProofMap(request.getToken()));
}
if (publicJsonWebKey.getPrivateKey() != null) {
LOG.warn("DPoP proof JWK key is a private key but it must be a public key");
throw new AuthenticationFailedException(invalidDPoPProofMap(request.getToken()));
}
byte[] jwkProofDigest = publicJsonWebKey.calculateThumbprint("SHA-256");
String jwkProofThumbprint = OidcCommonUtils.base64UrlEncode(jwkProofDigest);
if (!dpopJwkThumbprint.equals(jwkProofThumbprint)) {
LOG.warn("DPoP access token JWK thumbprint does not match the DPoP proof JWK thumbprint");
throw new AuthenticationFailedException(invalidDPoPProofMap(request.getToken()));
}
try {
JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmConstraints(OidcProvider.ASYMMETRIC_ALGORITHM_CONSTRAINTS);
jws.setCompactSerialization((String) requestData.get(OidcUtils.DPOP_PROOF));
jws.setKey(publicJsonWebKey.getPublicKey());
if (!jws.verifySignature()) {
LOG.warn("DPoP proof token signature is invalid");
throw new AuthenticationFailedException(invalidDPoPProofMap(request.getToken()));View on GitHub (pinned to e1c734241f)
Solutions
- Embed only the public part: use privateJwk.toPublicJwk().toJson() (jose4j) or equivalent before placing it in the jwk header.
- Audit the proof generator to ensure only kty/crv/x/y (EC) or kty/n/e (RSA) members are emitted.
- If a private key may have leaked in a proof, rotate the DPoP signing key.
- Catch AuthenticationFailedException and regenerate proofs with the public JWK.
Example fix
// before String jwkJson = privateJsonWebKey.toJson(); // includes private 'd' // after String jwkJson = privateJsonWebKey.toPublicJwk().toJson();
Defensive patterns
Strategy: validation
Validate before calling
// Assert no private members leak into the proof header
Set<String> priv = Set.of("d", "p", "q", "dp", "dq", "qi", "oth");
if (jwkMap.keySet().stream().anyMatch(priv::contains)) {
throw new IllegalStateException("Proof jwk header must contain only PUBLIC key material");
} Type guard
static boolean isPublicOnlyJwk(Map<String,Object> jwk) {
return !jwk.containsKey("d") && !jwk.containsKey("p") && !jwk.containsKey("q");
} Prevention
- Use toPublicJwk().toJson() (jose4j) rather than toJson() on a PrivateJsonWebKey
- Code-review any code that serializes key pairs
- Rotate the DPoP key if a proof with private material was transmitted
- Treat private-key-in-header incidents as security events, not just auth errors
When it happens
Trigger: Serializing a KeyPair/EC5PrivateKey into the jwk header (e.g. calling toJson on a PrivateJsonWebKey, which includes 'd' and other private parameters), so jose4j's parsed PublicJsonWebKey has a non-null privateKey.
Common situations: Using PrivateJsonWebKey.toJson() instead of toJson() on its public counterpart; storing a full JWK set from the keystore in the proof; libraries misconfigured to embed key pairs.
Related errors
- DPoP proof jwk header does not represent a valid JWK key
- DPoP access token does not contain a confirmation 'cnf' clai
- DPoP proof jwk header is missing
- DPoP access token JWK thumbprint does not match the DPoP pro
- DPoP proof token signature is invalid
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c3c56d84a8754345.
Report an issue: GitHub.