apereo/cas · warning
Authentication throttling rate
Error message
Authentication throttling rate [{}] exceeds the defined threshold [{}] What it means
calculateFailureThresholdRateAndCompare computes the instantaneous failure rate between the two most recent failed attempts (1 second divided by the millisecond difference). If that computed rate exceeds the configured threshold rate, this warning is logged and the request is throttled.
Solutions
- Slow down or de-duplicate client-side retries of failed authentication.
- Lower the configured threshold rate so only genuinely fast sequences trigger throttling.
- Block or rate-limit the offending client at a proxy/WAF layer.
- Review audit log timestamps to confirm whether the pattern is an attack.
Example fix
// before cas.authn.throttle.failure.threshold=3 cas.authn.throttle.failure.range-seconds=60 // after: threshold rate tuned so normal retries aren't throttled cas.authn.throttle.failure.threshold=15 cas.authn.throttle.failure.range-seconds=60
Defensive patterns
Strategy: validation
Validate before calling
// client: enforce a minimum gap between authentication attempts
if (lastAttempt != null && Instant.now().toEpochMilli() - lastAttempt < 1000)
Thread.sleep(1000); Prevention
- Debounce client retries; never retry failed auth in a tight loop.
- Calculate your effective threshold rate and test it before production.
- Review audit failure timestamps to distinguish attack from misconfiguration.
When it happens
Trigger: Two consecutive failed authentication attempts recorded so close together that (1000ms / gapMs) is greater than cas.authn.throttle's threshold rate — e.g. failures within a few milliseconds to a few hundred milliseconds of each other.
Common situations: Automated scripts submitting rapid-fire login attempts; application retry loops retrying bad credentials immediately; extremely sensitive threshold configuration making any quick double-submit trip the limiter.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Throttled submission
- Throttling submission from
- Validation attempt for principal is throttled
- Authentication handler is disabled
- No user can be accepted because none is defined
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/e115861a95e7869b.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-throttle-core/src/main/java/org/apereo/cas/throttle/AbstractThrottledSubmissionHandlerInterceptorAdapter.java:125
protected boolean shouldResponseBeRecordedAsFailure(final HttpServletResponse response) {
val status = response.getStatus();
return status != HttpStatus.CREATED.value()
&& status != HttpStatus.OK.value() && status != HttpStatus.FOUND.value();
}
protected void recordThrottle(final HttpServletRequest request) {
}
protected boolean calculateFailureThresholdRateAndCompare(final List<? extends ThrottledSubmission> failures) {
if (failures.size() >= 2) {
val lastTime = DateTimeUtils.dateOf(failures.getFirst().getValue()).getTime();
val secondToLastTime = DateTimeUtils.dateOf(failures.get(1).getValue()).getTime();
val difference = lastTime - secondToLastTime;
val rate = NUMBER_OF_MILLISECONDS_IN_SECOND / difference;
LOGGER.debug("Last attempt was at [{}] and the one before that was at [{}]. Difference is [{}] calculated as rate of [{}]",
lastTime, secondToLastTime, difference, rate);
if (rate > getThresholdRate()) {
LOGGER.warn("Authentication throttling rate [{}] exceeds the defined threshold [{}]", rate, getThresholdRate());
return true;
}
}
return false;
}
protected String getUsernameParameterFromRequest(final HttpServletRequest request) {
val throttle = getConfigurationContext().getCasProperties().getAuthn().getThrottle().getCore();
return request.getParameter(StringUtils.defaultIfBlank(throttle.getUsernameParameter(), "username"));
}
protected LocalDateTime getFailureInRangeCutOffDate() {
val throttle = getConfigurationContext().getCasProperties().getAuthn().getThrottle().getFailure();
return LocalDateTime.now(ZoneOffset.UTC).minusSeconds(throttle.getRangeSeconds());
}
protected void recordAuditAction(final HttpServletRequest request, final String actionName) {
val userToUse = getUsernameParameterFromRequest(request);View on GitHub (pinned to e7288fc434)