apereo/cas · error · FailedLoginException

Radius authentication failed

Error message

Radius authentication failed ${e.message}

What it means

The catch-all in RadiusAuthenticationHandler.authenticateUsernamePasswordInternal: any Throwable escaping the RADIUS exchange (network failure, protocol error, null response, etc.) is logged and rethrown as FailedLoginException('Radius authentication failed ' + cause message). The original exception type is lost, only its message survives.

Solutions

  1. Read the logged cause (LoggingUtils.error output) and fix the underlying RADIUS connectivity/configuration
  2. Check network reachability: telnet/nc to the RADIUS host and authentication port
  3. Verify the shared secret length and exact match; verify port 1812 (auth) vs 1813 (acct)
  4. Enable failoverOnException=true to fail over to other servers on exceptions

Example fix

// before (config)
// cas.authn.radius.failover-exception=false
// after
// cas.authn.radius.failover-exception=true
// and add a secondary server: cas.authn.radius.servers[1].address=radius2.example.com
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check reachability before auth attempt
new Socket().connect(new InetSocketAddress(radiusHost, 1812), 3000);

Try / catch

try {
    authHandler.authenticate(credential);
} catch (FailedLoginException e) {
    if (e.getMessage() != null && !e.getMessage().isBlank()) {
        // inspect embedded cause message: timeout / refused / secret
    }
}

Prevention

When it happens

Trigger: RadiusClient throws inside RadiusUtils (socket timeout, unknown host, shared-secret/encoding error, response parse issue) and failoverOnException is false, so the exception bubbles into this catch block and is converted to FailedLoginException with e.getMessage().

Common situations: RADIUS server down or firewalled (timeout); DNS failure for the RADIUS hostname; shared secret too short (RFC 2865 requires >=16 chars) causing client-side rejection; wrong port (1812 auth vs 1813 accounting).

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-radius/src/main/java/org/apereo/cas/adaptors/radius/authentication/handler/support/RadiusAuthenticationHandler.java:66

    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential,
                                                                                        final String originalPassword) throws GeneralSecurityException {

        try {
            val username = credential.getUsername();
            val result = RadiusUtils.authenticate(username, credential.toPassword(), this.servers,
                this.failoverOnAuthenticationFailure, this.failoverOnException, Optional.empty());
            if (result.getKey() && result.getValue().isPresent()) {
                val attributes = CollectionUtils.toMultiValuedMap(result.getValue().get());
                return createHandlerResult(credential,
                    principalFactory.createPrincipal(username, attributes),
                    new ArrayList<>());
            }
            throw new FailedLoginException("Radius authentication failed for user " + username);
        } catch (final Throwable e) {
            LoggingUtils.error(LOGGER, e);
            throw new FailedLoginException("Radius authentication failed " + e.getMessage());
        }
    }
}

View on GitHub (pinned to e7288fc434)