apereo/cas · error · FailedLoginException

Radius authentication failed for user

Error message

Radius authentication failed for user ${username}

What it means

RadiusTokenAuthenticationHandler.doAuthentication throws FailedLoginException when RadiusUtils.authenticateUsernamePassword returns success=false, meaning RADIUS rejected the credentials (and failover was disabled or exhausted). The username is interpolated into the message.

Solutions

  1. Confirm the token/OTP code is valid and not expired
  2. Validate cas.authn.mfa.radius.* server settings (host, shared secret, port)
  3. Enable failoverOnAuthenticationFailure/failoverOnException to retry alternate RADIUS servers
  4. Check RADIUS server-side logs to distinguish bad code vs server policy reject

Example fix

// before
throw new FailedLoginException("Radius authentication failed for user " + username);
// after (config): cas.authn.mfa.radius.failover-authentication-failure=true
// cas.authn.mfa.radius.failover-exception=true
Defensive patterns

Strategy: try-catch

Try / catch

try {
    handler.doAuthentication(credential);
} catch (FailedLoginException e) {
    // show 'invalid token code' message to the MFA user
}

Prevention

When it happens

Trigger: MFA radius-token flow calls RadiusUtils with the OTP/username credentials; the resulting Pair key is false because all RADIUS servers rejected, and failoverOnAuthenticationFailure was false or all servers were tried.

Common situations: Invalid or expired OTP/token code; misconfigured RADIUS server credentials in the MFA radius settings; RADIUS server unreachable and failoverOnException=false path already handled; user account rejected by policy server.

Understand the failure class

Related errors


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

Appendix: source

Thrown at support/cas-server-support-radius-mfa/src/main/java/org/apereo/cas/adaptors/radius/authentication/RadiusTokenAuthenticationHandler.java:91

        var state = Optional.empty();
        val attributes = principal.getAttributes();
        if (attributes.containsKey(Attr_State.NAME)) {
            LOGGER.debug("Found state attribute in principal attributes for multifactor authentication");
            val stateValue = CollectionUtils.firstElement(attributes.get(Attr_State.NAME));
            if (stateValue.isPresent()) {
                val stateAttr = (AttributeValue) stateValue.get();
                state = Optional.of(stateAttr.getValueObject());
            }
        }
        val result = RadiusUtils.authenticate(username, password, this.servers,
            failoverOnAuthenticationFailure, this.failoverOnException, state);
        if (result.getKey()) {
            val radiusAttributes = CollectionUtils.toMultiValuedMap(result.getValue().orElseThrow());
            val finalPrincipal = principalFactory.createPrincipal(username, radiusAttributes);
            return createHandlerResult(credential, finalPrincipal, new ArrayList<>());
        }
        throw new FailedLoginException("Radius authentication failed for user " + username);
    }
}

View on GitHub (pinned to e7288fc434)