quarkusio/quarkus · error · AuthenticationFailedException

Client certificate thumbprint is not available

Error message

Client certificate thumbprint is not available

What it means

This AuthenticationFailedException is thrown by Quarkus OIDC's OidcIdentityProvider when verifying an access token bound to an mTLS client certificate (RFC 8705). The token carries a 'cnf' claim with an 'x5t#S256' certificate thumbprint, but the certificate presented on the current TLS connection has no thumbprint available, so binding cannot be verified. The provider fails authentication rather than trusting an unverifiable certificate-bound token.

Source

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

            final TokenType tokenType = idToken ? TokenType.ID_TOKEN : TokenType.BEARER_ACCESS_TOKEN;
            Uni<TokenVerificationResult> result = verifyTokenUni(requestData, resolvedContext, request.getToken(), tokenType,
                    idToken, userInfo);
            if (!idToken) {
                if (resolvedContext.oidcConfig().token().binding().certificate()) {
                    result = result.onItem().transform(new Function<TokenVerificationResult, TokenVerificationResult>() {

                        @Override
                        public TokenVerificationResult apply(TokenVerificationResult t) {
                            String tokenCertificateThumbprint = getTokenCertThumbprint(requestData, t);
                            if (tokenCertificateThumbprint == null) {
                                LOG.warn(
                                        "Access token does not contain a confirmation 'cnf' claim with the certificate thumbprint");
                                throw new AuthenticationFailedException(tokenMap(request.getToken()));
                            }
                            String clientCertificateThumbprint = (String) requestData.get(OidcConstants.X509_SHA256_THUMBPRINT);
                            if (clientCertificateThumbprint == null) {
                                LOG.warn("Client certificate thumbprint is not available");
                                throw new AuthenticationFailedException(tokenMap(request.getToken()));
                            }
                            if (!clientCertificateThumbprint.equals(tokenCertificateThumbprint)) {
                                LOG.warn("Client certificate thumbprint does not match the token certificate thumbprint");
                                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);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Enable client certificate authentication so the thumbprint is populated: set quarkus.http.auth.certificate-role-attributes / ensure quarkus.http.ssl.client-auth=REQUIRED (or REQUEST) on the listener serving OIDC.
  2. If terminating TLS at a proxy, forward the client certificate and configure the recorder that populates X509_SHA256_THUMBPRINT (e.g. via a custom Vert.x peer certificate extraction or proxy header handling).
  3. Ensure the token issuer issues mTLS-bound tokens only for callers that actually present certificates; otherwise request a non-certificate-bound token.
  4. Catch AuthenticationFailedException in a custom authentication exception mapper and return 401 with guidance about presenting the client certificate.

Example fix

// before (proxy terminates TLS, Quarkus sees no cert)
# application.properties
quarkus.http.ssl.client-auth=NONE

// after
quarkus.http.ssl.client-auth=REQUIRED
# and at the proxy: pass through the client cert (e.g. PROXY protocol v2 with cert extension, or re-encrypt TLS)
Defensive patterns

Strategy: validation

Validate before calling

// Before calling the API, ensure mTLS is active and a cert thumbprint is present
if (sslSession.getPeerCertificates().length == 0) {
    throw new IllegalStateException("mTLS-bound token used without a client certificate");
}
// Optionally compare with token cnf.x5t#S256 yourself

Type guard

static boolean hasClientCert(SecurityContext ctx) {
    return ctx != null && ctx.getUserPrincipal() instanceof CertificatePrincipal;
}

Prevention

When it happens

Trigger: An access token whose 'cnf' claim contains an 'x5t#S256' thumbprint arrives on a request whose client certificate was not captured into OidcConstants.X509_SHA256_THUMBPRINT request data (e.g. mutual TLS not enabled on the HTTP listener, or the CertificateAuthRequestDataRecorder/vert.x peer certificate is absent because TLS terminates at a proxy).

Common situations: TLS offloaded at a load balancer or reverse proxy so Quarkus never sees the client certificate; quarkus.http.ssl.certificate files configured without client-auth=REQUIRED; testing over plain HTTP with an mTLS-bound token from a different environment.

Understand the failure class

Related errors


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