apereo/cas · error · AccountExpiredException
Could not authenticate expired account for
Error message
Could not authenticate expired account for ${username} What it means
RestAuthenticationHandler throws AccountExpiredException when the remote REST endpoint replies HTTP 412 PRECONDITION_FAILED. CAS maps this status to an expired account, meaning the account (or its password) is no longer valid by time-based policy.
Solutions
- Renew/extend the account or password validity in the remote system
- Have the user reset an expired password
- If the endpoint uses 412 for other preconditions, change the endpoint semantics or write a custom handler mapping
- Synchronize password-expiry policies between CAS and the remote store
Example fix
// before: remote account
{"user":"jdoe","expires":"2024-01-01"}
// after extension
{"user":"jdoe","expires":"2027-01-01"} Defensive patterns
Strategy: try-catch
Validate before calling
val acct = remoteUserStore.lookup(username);
if (acct != null && acct.getExpiresOn().isBefore(LocalDate.now())) {
throw new AccountExpiredException("Account expired");
} Type guard
boolean isPreconditionFailed(HttpResponse r) { return r != null && r.getCode() == 412; } Try / catch
try {
return restHandler.authenticate(credential);
} catch (AccountExpiredException e) {
LOGGER.info("Expired account: {}", e.getMessage());
throw e; // route to renewal/password-reset flow
} Prevention
- Run expiry-sync jobs between CAS and the remote store
- Alert on accounts nearing expiry so renewals happen proactively
- Keep 412 semantics reserved for expiry in the endpoint contract
- Test expiry behavior after password-policy changes
When it happens
Trigger: authenticateUsernamePasswordInternal receives HTTP 412 from the endpoint; the switch maps PRECONDITION_FAILED -> AccountExpiredException.
Common situations: Password aged out on the remote system; account validity/expiry date passed; the endpoint uses 412 for conditional-request failures unrelated to expiry, which CAS then misinterprets.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unable to accept response status
- No credentials can be extracted to authenticate the REST…
- Unable to extract credentials for multifactor authentication
- AccountExpiredException
- Could not authenticate forbidden account for
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/79252d812e2d5002.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-rest-authentication/src/main/java/org/apereo/cas/adaptors/rest/RestAuthenticationHandler.java:97
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);
val principalFromRest = MAPPER.readValue(result, Principal.class);
val principal = principalFactory.createPrincipal(principalFromRest.getId(), principalFromRest.getAttributes());
return createHandlerResult(credential, principal, getWarnings(response));View on GitHub (pinned to e7288fc434)