apereo/cas · warning

Token [ ] has exceeded the maximum number of attempts [ ]

Error message

Token [{}] has exceeded the maximum number of attempts [{}]

What it means

GoogleAuthenticatorAuthorizeTokenAttemptAction limits how many times a token can be submitted for verification within a webflow session. When the stored attemptCounter reaches maxAllowedAttempts (maxCheckAttempts minus allowed margin logic in the source), it warns 'Token [{}] has exceeded the maximum number of attempts [{}]' and returns an error event, blocking further attempts for that credential.

Solutions

  1. Increase cas.authn.mfa.gauth.max-check-attempts (or core token attempt settings) if legitimate users hit the cap
  2. Start a new authentication flow (restart the webflow) which resets the flow-scope attempt counter
  3. Investigate the repeated attempts in the log for brute-force patterns and consider rate limiting
  4. Verify the user's authenticator app clock drift if failures are legitimate

Example fix

// before
cas.authn.mfa.gauth.max-check-attempts=1
// after
cas.authn.mfa.gauth.max-check-attempts=5
Defensive patterns

Strategy: validation

Validate before calling

if (attemptCounter >= maxCheckAttempts) {
    return error(); // caller-side check before submitting another token
}

Try / catch

try { ... } catch (Exception e) { /* this is a webflow event, not exception; handle the 'error' transition by restarting the flow */ }

Prevention

When it happens

Trigger: A user (or an automated script) submits an incorrect Google Authenticator OTP credential to the webflow more times than the configured maximum during one authentication flow session.

Common situations: Brute-force attempts on OTP codes, users repeatedly mistyping codes, or very low maxCheckAttempts configuration making legitimate users hit the cap; token id in the log identifies the credential.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/web/flow/GoogleAuthenticatorAuthorizeTokenAttemptAction.java:39

@Slf4j
public class GoogleAuthenticatorAuthorizeTokenAttemptAction extends BaseCasWebflowAction {
    private static final String FLOW_SCOPE_ATTEMPT_COUNTER = "GoogleAuthenticatorTokenAttemptCount";
    
    private final CasConfigurationProperties casProperties;

    @Override
    protected @Nullable Event doExecuteInternal(final RequestContext requestContext) {
        val maxAllowedAttempts = casProperties.getAuthn().getMfa().getGauth().getCore().getMaximumAuthenticationAttempts();
        val credential = Objects.requireNonNull(WebUtils.getCredential(requestContext));
        var attemptCounter = requestContext.getFlowScope().get(FLOW_SCOPE_ATTEMPT_COUNTER, Integer.class, 0);
        LOGGER.debug("Attempt counter for token [{}] is [{}]", credential.getId(), attemptCounter);
        if (maxAllowedAttempts <= 0 || attemptCounter < maxAllowedAttempts) {
            LOGGER.debug("Token [{}] is allowed to proceed with authentication", credential.getId());
            attemptCounter++;
            requestContext.getFlowScope().put(FLOW_SCOPE_ATTEMPT_COUNTER, attemptCounter);
            return success(attemptCounter);
        }
        LOGGER.warn("Token [{}] has exceeded the maximum number of attempts [{}]", credential.getId(), attemptCounter);
        return error();
    }
}

View on GitHub (pinned to e7288fc434)