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

  1. Slow down or de-duplicate client-side retries of failed authentication.
  2. Lower the configured threshold rate so only genuinely fast sequences trigger throttling.
  3. Block or rate-limit the offending client at a proxy/WAF layer.
  4. 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

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

Related errors


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)