apereo/cas · error · FailedLoginException
Principal is null, the processing of the SPNEGO Token failed
Error message
Principal is null, the processing of the SPNEGO Token failed
What it means
After running the JCIFS SPNEGO/NTLM handshake, doInternalAuthentication expects theJCIFS library to produce a non-null java.security.Principal representing the authenticated user. If processing the client token yields no principal (success flag stays false), the token was invalid or the handshake failed, so FailedLoginException is thrown.
Solutions
- Verify jcifs properties: jcifs.smb.client.domain, domain controller address, and the service account password used by the authentications pool.
- Check SPN registration for the CAS host (setspn -L) matches the URL used by clients.
- Synchronize clocks with the KDC (kerberos tolerance is typically 5 minutes).
- Enable JCIFS debug logging (log4j config for jcifs) to see the underlying token-processing failure.
Example fix
// before cas.authn.spnego.jcifs.domain-controller=old-dc.corp.local // after cas.authn.spnego.jcifs.domain-controller=dc1.corp.local
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-checks: SPN + clock sync kvno HTTP/cas.example.org@CORP.LOCAL ntpdate -q dc1.corp.local
Try / catch
try {
return handler.authenticate(credential);
} catch (FailedLoginException e) {
if (e.getMessage().contains("Principal is null")) {
// verify jcifs DC/domain config and service account before surfacing to user
LOGGER.error("SPNEGO token processing failed; check JCIFS/Kerberos config");
}
throw e;
} Prevention
- Keep setspn registrations in sync with the CAS URL
- Synchronize clocks with the KDC (within 5 minutes)
- Store the service account password/keytab securely and test after rotation
- Enable jcifs debug logging in staging
When it happens
Trigger: Client presents a SPNEGO/NTLM token that JCIFS cannot validate: wrong credentials, replayed/expired token, misconfigured jcifs.spengo.* / kerberos settings, or the DC rejects the token.
Common situations: Incorrect service password/krb5 keytab; SPN mismatch between requested host and registered SPN; clock skew between CAS server and KDC; jcifs domain/controller settings pointing at the wrong DC.
Related errors
- NTLM not allowed
- Cannot get connection from pool to validate SPNEGO Token
- User Agent header [ ] is empty, or no browsers are supported
- SPNEGO Authorization header is not found under
- SPNEGO Authorization header
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/312bb66c1af661d1.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-spnego/src/main/java/org/apereo/cas/support/spnego/authentication/handler/support/JcifsSpnegoAuthenticationHandler.java:112
if (nextToken != null) {
LOGGER.debug("Setting nextToken in credential");
spnegoCredential.setNextToken(nextToken);
} else {
LOGGER.debug("nextToken is null");
}
var success = false;
if (principal != null) {
if (spnegoCredential.isNtlm()) {
LOGGER.debug("NTLM Credential is valid for user [{}]", principal.getName());
} else {
LOGGER.debug("Kerberos Credential is valid for user [{}]", principal.getName());
}
spnegoCredential.setPrincipal(getPrincipal(principal.getName(), spnegoCredential.isNtlm()));
success = true;
}
if (!success) {
throw new FailedLoginException("Principal is null, the processing of the SPNEGO Token failed");
}
return new DefaultAuthenticationHandlerExecutionResult(this, spnegoCredential, spnegoCredential.getPrincipal());
}
@Override
public boolean supports(final Credential credential) {
return credential instanceof SpnegoCredential;
}
@Override
public boolean supports(final Class<? extends Credential> clazz) {
return SpnegoCredential.class.isAssignableFrom(clazz);
}
protected @Nullable Principal getPrincipal(final String name, final boolean isNtlm) throws Throwable {
if (spnegoProperties.isPrincipalWithDomainName()) {
return this.principalFactory.createPrincipal(name);
}View on GitHub (pinned to e7288fc434)