apereo/cas · warning · FailedLoginException
Authorization of OTP token
Error message
Authorization of OTP token [{}] has failed What it means
The GoogleAuthenticatorAuthenticationHandler authenticated the OTP token format but the token was not valid for the user's registered account, so the handler logs a warning and throws FailedLoginException. This is the normal rejection path for an incorrect, expired, or already-used one-time code during multifactor login.
Solutions
- Have the user enter a freshly generated code from their authenticator app
- Check clock synchronization (NTP) on both the server and the user's device, and review cas.authn.mfa.gauth.core.* window settings
- Verify the account record exists in the token repository for the authenticating principal (scratch codes/secret correct)
- Clear any previously stored tokens if token reuse prevention is rejecting valid codes
Example fix
// before (server rejects all codes due to clock drift) // cas.authn.mfa.gauth.core.time-step-size=30, window unchanged // after: widen validation window in cas.properties cas.authn.mfa.gauth.core.window-size=5
Defensive patterns
Strategy: validation
Validate before calling
// before relying on login succeeding, ensure an account exists for the user Collection<? extends OneTimeTokenAccount> accts = repository.get(username); boolean canAttempt = (accts != null && !accts.isEmpty());
Try / catch
try {
handlerResult = handler.doAuthentication(credential, service, appContext);
} catch (FailedLoginException e) {
// treat as invalid OTP: prompt user for a fresh code, do not lock immediately
} Prevention
- Keep server clocks NTP-synchronized
- Tune cas.authn.mfa.gauth.core window-size for realistic clock drift
- Educate users to enter freshly generated codes
- Monitor FailedLoginException rates to detect repository/secret corruption
When it happens
Trigger: doAuthentication calls validator.validate() which returns null (token does not match any window/scratch code for the user's stored secret, was already used, or no account exists); the handler then logs 'Authorization of OTP token has failed' and throws.
Common situations: User mistypes or mistranscribes the 6-digit code from their authenticator app; device clock drift pushes the code outside the validation window; the same token was already consumed (one-time use enforcement); the user's account secret was re-registered but the client still shows old codes.
Related errors
- Unable to extract credentials for multifactor authentication
- Duo Security authentication has failed
- Failed to authenticate code
- cannot be found in the registry
- cannot reuse OTP
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/56e3515b319d282c.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/GoogleAuthenticatorAuthenticationHandler.java:71
@Override
public boolean supports(final Credential credential) {
return GoogleAuthenticatorTokenCredential.class.isAssignableFrom(credential.getClass());
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential, final Service service) throws Throwable {
val tokenCredential = (GoogleAuthenticatorTokenCredential) credential;
val authentication = Objects.requireNonNull(WebUtils.getInProgressAuthentication());
Objects.requireNonNull(authentication, "No authentication is available to determine the principal");
val validatedToken = validator.validate(authentication, tokenCredential);
if (validatedToken != null) {
val principal = authentication.getPrincipal().getId();
LOGGER.debug("Validated OTP token [{}] successfully for [{}]", validatedToken, principal);
validator.store(validatedToken);
LOGGER.debug("Creating authentication result and building principal for [{}]", principal);
return createHandlerResult(tokenCredential, principalFactory.createPrincipal(principal));
}
LOGGER.warn("Authorization of OTP token [{}] has failed", credential);
throw new FailedLoginException("Failed to authenticate code " + credential);
}
}
View on GitHub (pinned to e7288fc434)