apereo/cas · error · FailedLoginException

Either client certificate could not be determined, or a…

Error message

Either client certificate could not be determined, or a trusted issuer could not be located

What it means

X509CredentialsAuthenticationHandler could not complete X.509 authentication because either no client certificate could be extracted from the presented credentials or the certificate was not issued by a trusted issuer in the configured trust store. The handler logs a warning and throws FailedLoginException, failing the authentication attempt.

Solutions

  1. Import the issuing CA (and intermediates) of the client certificate into the configured X.509 trust store/keystore so hasTrustedIssuer becomes true.
  2. Ensure the TLS connector/web server actually requests and forwards the client certificate (clientAuth=want/need and correct header forwarding behind proxies).
  3. Check cas.authn.x509 trust store configuration paths and passwords; confirm the trust managers are properly initialized rather than null.
  4. Enable debug logging in X509CredentialsAuthenticationHandler and inspect why clientCert extraction or issuer validation failed, then fix the specific mismatch.

Example fix

// before: clientAuth="false" on the Tomcat connector
<Connector ... clientAuth="want" truststoreFile="/etc/cas/truststore.jks" />
// after
<Connector ... clientAuth="true" truststoreFile="/etc/cas/truststore.jks" />
Defensive patterns

Strategy: validation

Validate before calling

// Guard before invoking the handler:
val certs = x509Credential.getCertificates();
if (certs == null || certs.length == 0) {
    throw new FailedLoginException("No client certificate presented");
}
val issuerDn = certs[0].getIssuerX500Principal().getName();
if (!trustedIssuers.contains(issuerDn)) {
    throw new FailedLoginException("Issuer not trusted: " + issuerDn);
}

Try / catch

try {
    return handler.authenticate(x509Credential);
} catch (FailedLoginException e) {
    logger.warn("X509 authentication failed: check client cert presence and trust store", e);
    throw e;
}

Prevention

When it happens

Trigger: doAuthentication checks hasTrustedIssuer (the certificate's issuer is in the configured trust managers) and clientCert != null; if either condition is false - e.g. trustManagers are null because none were configured, the issuer is not in the trust store, or the client certificate could not be determined from the X509CertificateCredential - this branch logs and throws.

Common situations: TLS mutual-auth config sets the client auth to optional and the browser/app sends no client certificate; the CA that signed the client cert is missing from the configured trustStore (keystore); intermediate CA not imported so chain validation fails; misconfigured cas.authn.x509 trust store settings.

Understand the failure class

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/2bdccfc3388436ea. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-x509-core/src/main/java/org/apereo/cas/adaptors/x509/authentication/handler/support/X509CredentialsAuthenticationHandler.java:225

            if (!hasTrustedIssuer) {
                hasTrustedIssuer = isCertificateFromTrustedIssuer(certificate);
            }

            val pathLength = certificate.getBasicConstraints();
            if (pathLength < 0) {
                LOGGER.debug("Found valid client certificate");
                clientCert = certificate;
            } else {
                LOGGER.debug("Found valid CA certificate");
            }
        }
        if (hasTrustedIssuer && clientCert != null) {
            x509Credential.setCertificate(clientCert);
            return new DefaultAuthenticationHandlerExecutionResult(this, x509Credential,
                this.principalFactory.createPrincipal(x509Credential.getId()));
        }
        LOGGER.warn("Either client certificate could not be determined, or a trusted issuer could not be located");
        throw new FailedLoginException();
    }

    private void validate(final X509Certificate cert) throws GeneralSecurityException {
        cert.checkValidity();
        this.revocationChecker.check(cert);

        val pathLength = cert.getBasicConstraints();
        if (pathLength < 0) {
            if (!isCertificateAllowed(cert)) {
                val msg = "Certificate subject does not match pattern " + this.regExSubjectDnPattern.pattern();
                LOGGER.error(msg);
                throw new FailedLoginException(msg);
            }
            if (this.checkKeyUsage && !isValidKeyUsage(cert)) {
                val msg = "Certificate keyUsage constraint forbids SSL client authentication.";
                LOGGER.error(msg);
                throw new FailedLoginException(msg);

View on GitHub (pinned to e7288fc434)