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

  1. Verify jcifs properties: jcifs.smb.client.domain, domain controller address, and the service account password used by the authentications pool.
  2. Check SPN registration for the CAS host (setspn -L) matches the URL used by clients.
  3. Synchronize clocks with the KDC (kerberos tolerance is typically 5 minutes).
  4. 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

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


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)