quarkusio/quarkus · error · AuthenticationFailedException

DPoP proof jwk header does not represent a valid JWK key

Error message

DPoP proof jwk header does not represent a valid JWK key

What it means

Thrown when the DPoP proof's 'jwk' header is present but its contents cannot be parsed as a valid public JWK by jose4j (PublicJsonWebKey.Factory.newPublicJwk throws JoseException). The proof's key material is malformed or of an unsupported type, so verification cannot proceed.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java:275

                                LOG.warn(
                                        "DPoP access token does not contain a confirmation 'cnf' claim with the JWK thumbprint");
                                throw new AuthenticationFailedException(invalidDPoPProofMap(request.getToken()));
                            }

                            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);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Emit a standards-compliant JWK object in the header: kty, and for EC kty=EC with crv/x/y, for RSA kty=RSA with n/e, base64url-encoded without padding.
  2. Use an established JOSE library (jose4j, nimbus-jose-jwt) to build the proof instead of hand-rolling the header.
  3. Avoid unsupported key types/curves; prefer ES256 (P-256) which Quarkus and ASes widely support.
  4. Validate locally with PublicJsonWebKey.Factory.newPublicJwk(json) before sending the proof.

Example fix

// before: PEM string in header
"jwk": "-----BEGIN PUBLIC KEY-----..."

// after: proper JWK object
"jwk": {"kty":"EC","crv":"P-256","x":"...","y":"..."}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate the JWK with jose4j before sending the proof
try {
    PublicJsonWebKey.Factory.newPublicJwk(jwkMap);
} catch (JoseException e) {
    throw new IllegalArgumentException("Proof jwk header is not a valid public JWK", e);
}

Type guard

static boolean isValidPublicJwk(Map<String,Object> jwk) {
    try { PublicJsonWebKey.Factory.newPublicJwk(jwk); return true; }
    catch (JoseException e) { return false; }
}

Try / catch

try {
    return callWithProof(buildProof());
} catch (AuthenticationFailedException e) {
    throw new IllegalStateException("Fix proof jwk: use kty EC (crv/x/y) or RSA (n/e), base64url", e);
}

Prevention

When it happens

Trigger: The jwk header map has missing/incorrect members (wrong 'kty', missing 'crv' for EC, bad base64url 'n'/'e' for RSA), or the JSON is not a JWK at all (e.g. a PEM string or certificate object pasted in).

Common situations: Manually serializing a java.security.PublicKey incorrectly; using an unsupported curve/algorithm; double-encoding the JWK as a JSON string instead of an object inside the header.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/d62672329da9392f. Report an issue: GitHub.