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
- Import the issuing CA (and intermediates) of the client certificate into the configured X.509 trust store/keystore so hasTrustedIssuer becomes true.
- Ensure the TLS connector/web server actually requests and forwards the client certificate (clientAuth=want/need and correct header forwarding behind proxies).
- Check cas.authn.x509 trust store configuration paths and passwords; confirm the trust managers are properly initialized rather than null.
- 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
- Import the full CA chain (root + intermediates) into the X.509 trust store.
- Set the web server/TLS connector to require client certificates where mTLS is mandatory.
- Forward the client certificate header correctly when terminating TLS on a reverse proxy.
- Keep trust-store configuration per-environment validated in deployment checks.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Invalid credentials:
- Duo Security passcode authentication has failed
- Duo Security universal prompt authentication has failed
- Unable to accept cookie for authentication
- Cannot validate authentication for: [login]
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)