quarkusio/quarkus · error · AuthenticationFailedException

DPoP access token does not contain a confirmation 'cnf' clai

Error message

DPoP access token does not contain a confirmation 'cnf' claim with the JWK thumbprint

What it means

Thrown during DPoP (RFC 9449) proof-of-possession verification: the access token must contain a 'cnf' claim with a 'jkt' (JWK thumbprint) value binding it to the sender's DPoP key, but no such claim exists. Quarkus then rejects the DPoP-bound request because the token cannot be tied to the proof's public key.

Source

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

                                throw new AuthenticationFailedException(tokenMap(request.getToken()));
                            }
                            return t;
                        }

                    });
                }

                if (requestData.containsKey(OidcUtils.DPOP_PROOF_JWT_HEADERS)) {
                    result = result.onItem().transform(new Function<TokenVerificationResult, TokenVerificationResult>() {

                        @Override
                        public TokenVerificationResult apply(TokenVerificationResult t) {

                            String dpopJwkThumbprint = getDpopJwkThumbprint(requestData, t);
                            if (dpopJwkThumbprint == null) {
                                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()));
                            }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Obtain the access token by sending a DPoP proof JWT with the token request so the AS embeds cnf.jkt (quarkus.oidc-client with DPoP enabled, or add the DPoP header to the token call).
  2. Ensure quarkus.oidc.token.verify-access-token-with-user-info / DPoP settings are consistent: if DPoP proofs are sent, tokens must be DPoP-bound by the AS.
  3. Disable DPoP proof generation on the HTTP client if the token is not DPoP-bound.
  4. Catch AuthenticationFailedException and re-authenticate to obtain a DPoP-bound token.

Example fix

// before: plain token + DPoP proof header
client.post("/protected").putHeader("DPoP", proof(token));

// after: request DPoP-bound token
// token request also carries a DPoP proof (htiu=token endpoint) so cnf.jkt is issued
client.post("/token").putHeader("DPoP", proofForTokenEndpoint(...));
Defensive patterns

Strategy: validation

Validate before calling

// Check the token is DPoP-bound before sending a DPoP proof
JsonWebToken jwt = parseUnverified(token);
var cnf = jwt.getClaimValue("cnf", Map.class);
if (cnf == null || cnf.get("jkt") == null) {
    token = obtainDpopBoundToken(); // include a DPoP proof in the token request
}

Type guard

static boolean isDpopBoundToken(JsonWebToken t) {
    Map<String,Object> cnf = t.getClaimValue("cnf", Map.class);
    return cnf != null && cnf.containsKey("jkt");
}

Try / catch

try {
    return callWithDpop(token);
} catch (AuthenticationFailedException e) {
    return callWithDpop(refreshDpopBoundToken());
}

Prevention

When it happens

Trigger: A request includes a DPoP proof header, so the provider runs DPoP verification, but the access token was issued without DPoP binding (no cnf.jkt) — e.g. the token was obtained via a plain client_credentials/token call instead of one that sends the DPoP proof JWT to the token endpoint.

Common situations: Mixing a normal (non-DPoP) access token with a DPoP proof header added by an HTTP client library that enables DPoP globally; authorization server configured to issue DPoP tokens only for some clients; token obtained before enabling DPoP on the client.

Related errors


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