apereo/cas · error · FailedLoginException
Could not authenticate account for
Error message
Could not authenticate account for ${username} What it means
RestAuthenticationHandler throws FailedLoginException when the remote REST authentication endpoint replies HTTP 401 UNAUTHORIZED. This means the credentials were rejected as invalid — the standard 'bad username or password' outcome for REST authentication.
Solutions
- Confirm the user's username/password are correct
- Check cas.authn.rest.password-encoder settings match what the endpoint expects (plain vs encoded)
- If the endpoint itself requires basic auth, verify the configured basicAuthUsername/basicAuthPassword are valid
- Inspect endpoint logs to see whether 401 came from user validation or endpoint authentication
Example fix
// before POST /auth -> 401 Unauthorized (wrong password) // after POST /auth -> 200 OK (correct credentials)
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate credentials format client-side before the call
if (username == null || username.isBlank() || password == null || password.isEmpty()) {
throw new IllegalArgumentException("Username and password required");
} Type guard
boolean isUnauthorized(HttpResponse r) { return r != null && r.getCode() == 401; } Try / catch
try {
return restHandler.authenticate(credential);
} catch (FailedLoginException e) {
LOGGER.debug("Bad credentials for REST auth: {}", e.getMessage());
throw e; // let CAS throttling/warning machinery handle it
} Prevention
- Match password-encoding config (cas.authn.rest.password-encoder) with the endpoint's expectation
- Verify any configured basic-auth credentials for the endpoint itself are current
- Test with a known-good credential after endpoint changes
- Check endpoint logs to distinguish user-vs-endpoint 401s
When it happens
Trigger: authenticateUsernamePasswordInternal sends the encoded credentials to the REST endpoint; it responds 401 and the switch maps UNAUTHORIZED -> FailedLoginException.
Common situations: User typed a wrong password; client and server disagree on password encoding (e.g. the endpoint expects raw but CAS sends encoded); credentials for the endpoint's own basic-auth are wrong so 401 reflects the endpoint, not the user.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- [ ] Caused by: [ ]
- Unable to accept response status
- No credentials can be extracted to authenticate the REST…
- Unable to extract credentials for multifactor authentication
- Could not authenticate provided credentials
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/776f90c0cad192ca.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-rest-authentication/src/main/java/org/apereo/cas/adaptors/rest/RestAuthenticationHandler.java:94
final String originalPassword) throws Throwable {
var response = (HttpResponse) null;
try {
val exec = HttpExecutionRequest
.builder()
.basicAuthUsername(credential.getUsername())
.basicAuthPassword(credential.toPassword())
.method(HttpMethod.valueOf(properties.getMethod().toUpperCase(Locale.ENGLISH)))
.url(SpringExpressionLanguageValueResolver.getInstance().resolve(properties.getUri()))
.httpClient(httpClient)
.build()
.withoutRetry();
response = HttpUtils.execute(exec);
val status = HttpStatus.resolve(Objects.requireNonNull(response).getCode());
return switch (Objects.requireNonNull(status)) {
case OK -> buildPrincipalFromResponse(credential, response);
case FORBIDDEN -> throw new AccountDisabledException("Could not authenticate forbidden account for " + credential.getUsername());
case UNAUTHORIZED -> throw new FailedLoginException("Could not authenticate account for " + credential.getUsername());
case NOT_FOUND -> throw new AccountNotFoundException("Could not locate account for " + credential.getUsername());
case LOCKED -> throw new AccountLockedException("Could not authenticate locked account for " + credential.getUsername());
case PRECONDITION_FAILED -> throw new AccountExpiredException("Could not authenticate expired account for " + credential.getUsername());
case PRECONDITION_REQUIRED -> throw new AccountPasswordMustChangeException("Account password must change for " + credential.getUsername());
default -> throw new FailedLoginException("Rest endpoint returned an unknown status code " + status + " for " + credential.getUsername());
};
} finally {
HttpUtils.close(response);
}
}
protected AuthenticationHandlerExecutionResult buildPrincipalFromResponse(
final UsernamePasswordCredential credential,
final HttpResponse response) throws Throwable {
try {
try (val content = ((HttpEntityContainer) response).getEntity().getContent()) {
val result = IOUtils.toString(content, StandardCharsets.UTF_8);
LOGGER.debug("REST authentication response received: [{}]", result);View on GitHub (pinned to e7288fc434)