apereo/cas · error · FailedLoginException

Unable to locate principal for token

Error message

Unable to locate principal for token [{}]

What it means

BaseCasSimpleMultifactorAuthenticationService.validateTokenForPrincipal() requires the CasSimpleMultifactorAuthenticationTicket to carry a PROPERTY_PRINCIPAL attribute. If the ticket has no principal property, it logs this warning, deletes the token from the ticket registry, and throws FailedLoginException. The token is unusable because there is no identity to compare against.

Solutions

  1. Discard the invalid code and restart the MFA flow to mint a new token with the principal property
  2. Inspect the ticket registry entry for PROPERTY_PRINCIPAL and remove stale/corrupt entries
  3. Check for ticket-registry data from an incompatible CAS version and flush the registry
  4. Review any custom code that creates CasSimpleMultifactorAuthenticationTicket instances to ensure the principal property is always set
Defensive patterns

Strategy: try-catch

Validate before calling

CasSimpleMultifactorAuthenticationTicket t =
    (CasSimpleMultifactorAuthenticationTicket) ticketRegistry.getTicket(tokenId);
if (t != null && !t.getProperties().containsKey(
        CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL)) {
    // token is corrupt; require a new one
}

Try / catch

try {
    service.validateTokenForPrincipal(resolvedPrincipal, ticket);
} catch (FailedLoginException e) {
    // reject code and restart MFA flow
}

Prevention

When it happens

Trigger: Validating a simple-MFA token whose authentication ticket was created without the principal property — e.g. a corrupted or manually fabricated ticket, or a ticket from an older/CAS version whose properties map lacks the principal entry.

Common situations: Ticket registry entries migrated from a different CAS version missing new properties; direct manipulation or partial serialization of ticket data in an external store (Redis/JDBC); bug in custom code constructing the MFA ticket.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-simple-mfa-core/src/main/java/org/apereo/cas/mfa/simple/validation/BaseCasSimpleMultifactorAuthenticationService.java:35

 *
 * @author Misagh Moayyed
 * @since 7.2.0
 */
@Slf4j
@RequiredArgsConstructor
public abstract class BaseCasSimpleMultifactorAuthenticationService implements CasSimpleMultifactorAuthenticationService {
    protected final TicketRegistry ticketRegistry;

    @Override
    public CasSimpleMultifactorAuthenticationTicket getMultifactorAuthenticationTicket(final CasSimpleMultifactorTokenCredential credential) {
        val tokenId = normalize(credential.getId());
        return ticketRegistry.getTicket(tokenId, CasSimpleMultifactorAuthenticationTicket.class);
    }

    protected Principal validateTokenForPrincipal(final Principal resolvedPrincipal, final CasSimpleMultifactorAuthenticationTicket acct)
        throws FailedLoginException {
        if (!acct.getProperties().containsKey(CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL)) {
            LOGGER.warn("Unable to locate principal for token [{}]", acct.getId());
            deleteToken(acct);
            throw new FailedLoginException("Failed to authenticate code " + acct.getId());
        }
        val principal = (Principal) acct.getProperties().get(CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL);
        if (!principal.equals(resolvedPrincipal)) {
            LOGGER.warn("Principal assigned to token [{}] is unauthorized for token [{}]", principal.getId(), acct.getId());
            deleteToken(acct);
            throw new FailedLoginException("Failed to authenticate code " + acct.getId());
        }
        return principal;
    }

    protected static String normalize(final String tokenId) {
        if (!tokenId.startsWith(CasSimpleMultifactorAuthenticationTicket.PREFIX)) {
            return CasSimpleMultifactorAuthenticationTicket.PREFIX + UniqueTicketIdGenerator.SEPARATOR + tokenId;
        }
        return tokenId;
    }

View on GitHub (pinned to e7288fc434)