apereo/cas · error · FailedLoginException
Failed to authenticate code
Error message
Failed to authenticate code
What it means
BaseCasSimpleMultifactorAuthenticationService.validateTokenForPrincipal throws FailedLoginException "Failed to authenticate code <id>" when the stored MFA authentication ticket has no PROPERTY_PRINCIPAL entry. Without the principal recorded on the token, the service cannot confirm the code belongs to the presenting user, so it deletes the token and fails the attempt.
Solutions
- Regenerate the MFA token through the standard flow so the principal property is set
- Inspect the ticket registry record for the token and confirm PROPERTY_PRINCIPAL exists
- Upgrade/align all CAS nodes to the same version so ticket serialization is consistent
- Clear stale/corrupted tokens from the registry and have the user restart the MFA step
Defensive patterns
Strategy: retry
Validate before calling
// verify the token record actually carries the principal property before validating
var acct = (CasSimpleMultifactorAuthenticationTicket) ticketRegistry.getTicket(tokenId);
if (acct == null || !acct.getProperties().containsKey(
CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL)) {
throw new IllegalStateException("Token missing principal property; request a new code");
} Try / catch
try {
principal = mfaService.validate(resolvedPrincipal, credential);
} catch (FailedLoginException e) {
LOGGER.warn("MFA token invalid ({}); restarting MFA with a fresh code", e.getMessage());
restartMfaFlow(resolvedPrincipal);
} Prevention
- Always mint simple-MFA tokens via the standard CAS flow so PROPERTY_PRINCIPAL is set
- Keep all CAS nodes on the same version to avoid ticket-registry serialization gaps
- Treat "principal missing" tokens as stale and never retry them
When it happens
Trigger: validate -> validateTokenForPrincipal when acct.getProperties() lacks CasSimpleMultifactorAuthenticationConstants.PROPERTY_PRINCIPAL — i.e., the token was created without the principal property, was corrupted, or is a fabricated/foreign ticket ID.
Common situations: A token was minted by a path that did not attach the principal property (custom/REST flows); ticket-registry data loss or serialization issues stripping properties; attacker probing with guessed token IDs; version-upgrade migration where older tokens lack the property.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Duo Security passcode authentication has failed
- Duo Security universal prompt authentication has failed
- Cannot validate authentication for: [login]
- Failed to authenticate code
- State [ : : ] does not have a matching transition for
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/687801307e5628fd.
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:37
* @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;
}
protected void deleteToken(final CasSimpleMultifactorAuthenticationTicket acct) {View on GitHub (pinned to e7288fc434)