{"record":{"id":"d58bf24efbb90c8e","repo":"apereo/cas","slug":"validation-attempt-for-principal-is-throttled","errorCode":null,"errorMessage":"Validation attempt for principal  is throttled","messagePattern":"Validation attempt for principal  is throttled","errorType":"exception","errorClass":"FailedLoginException","httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-simple-mfa-core/src/main/java/org/apereo/cas/mfa/simple/validation/DefaultCasSimpleMultifactorAuthenticationService.java","lineNumber":97,"sourceCode":"\n    @Override\n    public Principal fetch(final CasSimpleMultifactorTokenCredential tokenCredential) {\n        return Optional.ofNullable(getMultifactorAuthenticationTicket(tokenCredential))\n            .map(this::getPrincipalFromTicket)\n            .orElse(null);\n    }\n\n    @Override\n    public void update(final Principal principal, final Map<String, Object> attributes) {\n        accountServiceProvider.ifAvailable(service -> service.update(principal, attributes));\n    }\n\n    @Override\n    public Principal validate(final Principal resolvedPrincipal,\n                              final CasSimpleMultifactorTokenCredential credential) throws Exception {\n        val result = bucketConsumer.consume(resolvedPrincipal.getId());\n        if (!result.isConsumed()) {\n            throw new FailedLoginException(\"Validation attempt for principal \" + resolvedPrincipal.getId() + \" is throttled\");\n        }\n        val acct = getMultifactorAuthenticationTicket(credential);\n        LOGGER.debug(\"Received token [{}] and principal id [{}]\", acct, resolvedPrincipal.getId());\n        val principal = validateTokenForPrincipal(resolvedPrincipal, acct);\n        deleteToken(acct);\n        LOGGER.debug(\"Validated token [{}] successfully for [{}].\", credential.getId(), resolvedPrincipal.getId());\n        return principal;\n    }\n}\n","sourceCodeStart":79,"sourceCodeEnd":107,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-simple-mfa-core/src/main/java/org/apereo/cas/mfa/simple/validation/DefaultCasSimpleMultifactorAuthenticationService.java#L79-L107","documentation":"DefaultCasSimpleMultifactorAuthenticationService.validate consumes a token from a bucket4j rate-limit bucket keyed by the principal id before validating the MFA code. If the bucket has no tokens left (result.isConsumed() false), it throws FailedLoginException \"Validation attempt for principal <id> is throttled\". This is rate limiting on MFA code attempts, protecting against brute-force code guessing.","triggerScenarios":"validate() when bucketConsumer.consume(principalId) returns a result with isConsumed()==false — the user (or an attacker against their id) exhausted the configured MFA attempt rate limit within the refill window.","commonSituations":"Users repeatedly submitting wrong/expired codes; shared NAT or scripted clients hammering validation; rate-limit window configured too small (few attempts, long refill) causing legitimate lockouts; automated tests tripping the limiter.","solutions":["Wait for the rate-limit window to refill and retry with a fresh code","Raise cas.authn.mfa.simple.core.rate-limit attempt count or shorten the refill period if too strict","Have the user request a new MFA code rather than retrying a stale one","Investigate repeated failures for the principal id as a possible credential-stuffing attempt"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    return mfaService.validate(resolvedPrincipal, credential);\n} catch (FailedLoginException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"is throttled\")) {\n        // back off until the bucket refills, then retry with a fresh code\n        Thread.sleep(backoffMillis);\n        return retryWithFreshCode(resolvedPrincipal);\n    }\n    throw e;\n}","preventionTips":["Tune cas.authn.mfa.simple rate-limit attempts/window to realistic retry behavior","Show users a lockout message with retry timing instead of a generic failure","Alert on throttling spikes — they often indicate brute-force attempts","Don't auto-retry aggressively; exponential backoff avoids compounding the throttle"],"tags":["mfa","rate-limiting","throttling","security"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}