{"record":{"id":"9e03bb3f2b14413d","repo":"quarkusio/quarkus","slug":"client-certificate-thumbprint-is-not-available","errorCode":null,"errorMessage":"Client certificate thumbprint is not available","messagePattern":"Client certificate thumbprint is not available","errorType":"exception","errorClass":"AuthenticationFailedException","httpStatus":401,"severity":"error","filePath":"extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java","lineNumber":237,"sourceCode":"            final TokenType tokenType = idToken ? TokenType.ID_TOKEN : TokenType.BEARER_ACCESS_TOKEN;\n            Uni<TokenVerificationResult> result = verifyTokenUni(requestData, resolvedContext, request.getToken(), tokenType,\n                    idToken, userInfo);\n            if (!idToken) {\n                if (resolvedContext.oidcConfig().token().binding().certificate()) {\n                    result = result.onItem().transform(new Function<TokenVerificationResult, TokenVerificationResult>() {\n\n                        @Override\n                        public TokenVerificationResult apply(TokenVerificationResult t) {\n                            String tokenCertificateThumbprint = getTokenCertThumbprint(requestData, t);\n                            if (tokenCertificateThumbprint == null) {\n                                LOG.warn(\n                                        \"Access token does not contain a confirmation 'cnf' claim with the certificate thumbprint\");\n                                throw new AuthenticationFailedException(tokenMap(request.getToken()));\n                            }\n                            String clientCertificateThumbprint = (String) requestData.get(OidcConstants.X509_SHA256_THUMBPRINT);\n                            if (clientCertificateThumbprint == null) {\n                                LOG.warn(\"Client certificate thumbprint is not available\");\n                                throw new AuthenticationFailedException(tokenMap(request.getToken()));\n                            }\n                            if (!clientCertificateThumbprint.equals(tokenCertificateThumbprint)) {\n                                LOG.warn(\"Client certificate thumbprint does not match the token certificate thumbprint\");\n                                throw new AuthenticationFailedException(tokenMap(request.getToken()));\n                            }\n                            return t;\n                        }\n\n                    });\n                }\n\n                if (requestData.containsKey(OidcUtils.DPOP_PROOF_JWT_HEADERS)) {\n                    result = result.onItem().transform(new Function<TokenVerificationResult, TokenVerificationResult>() {\n\n                        @Override\n                        public TokenVerificationResult apply(TokenVerificationResult t) {\n\n                            String dpopJwkThumbprint = getDpopJwkThumbprint(requestData, t);","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java#L219-L255","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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.","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).","Ensure the token issuer issues mTLS-bound tokens only for callers that actually present certificates; otherwise request a non-certificate-bound token.","Catch AuthenticationFailedException in a custom authentication exception mapper and return 401 with guidance about presenting the client certificate."],"exampleFix":"// before (proxy terminates TLS, Quarkus sees no cert)\n# application.properties\nquarkus.http.ssl.client-auth=NONE\n\n// after\nquarkus.http.ssl.client-auth=REQUIRED\n# and at the proxy: pass through the client cert (e.g. PROXY protocol v2 with cert extension, or re-encrypt TLS)","handlingStrategy":"validation","validationCode":"// Before calling the API, ensure mTLS is active and a cert thumbprint is present\nif (sslSession.getPeerCertificates().length == 0) {\n    throw new IllegalStateException(\"mTLS-bound token used without a client certificate\");\n}\n// Optionally compare with token cnf.x5t#S256 yourself","typeGuard":"static boolean hasClientCert(SecurityContext ctx) {\n    return ctx != null && ctx.getUserPrincipal() instanceof CertificatePrincipal;\n}","tryCatchPattern":null,"preventionTips":["Set quarkus.http.ssl.client-auth=REQUIRED for endpoints consuming cert-bound tokens","Use TLS passthrough at proxies instead of termination, or forward client certs","Only request cert-bound tokens (RFC 8705) from ASes when you always present the cert","Smoke-test over the real TLS topology, not just localhost"],"tags":["oidc","mtls","certificate-bound-token","authentication"],"backgroundTag":"mtls-client-certificate-missing","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}