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
- Discard the invalid code and restart the MFA flow to mint a new token with the principal property
- Inspect the ticket registry entry for PROPERTY_PRINCIPAL and remove stale/corrupt entries
- Check for ticket-registry data from an incompatible CAS version and flush the registry
- 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
- Flush ticket registries after CAS version upgrades
- Never hand-craft or edit ticket data in external stores
- Ensure any custom ticket-creation code sets PROPERTY_PRINCIPAL
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
- Validation attempt for principal is throttled
- No registered devices for multifactor authentication could…
- Credential principal
- Principal assigned to token
- State [ : : ] does not have a matching transition for
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)