apereo/cas · error · AccountExpiredException
AccountExpiredException
Error message
AccountExpiredException
What it means
When the Redis account's Status is EXPIRED, the handler throws AccountExpiredException after successful password verification. Authentication is refused because the account's validity window has lapsed per the stored status.
Solutions
- Extend the account's validity/renewal upstream and re-sync status to OK in Redis
- Manually update the RedisUserAccount status to OK if the upstream system is already correct
- Review the expiry/sync job schedule so renewals propagate promptly
Example fix
// before account.setStatus(Status.EXPIRED); redisTemplate.opsForValue().set(username, account); // after (post-renewal sync) account.setStatus(Status.OK); redisTemplate.opsForValue().set(username, account);
Defensive patterns
Strategy: validation
Validate before calling
RedisUserAccount acct = (RedisUserAccount) redisTemplate.opsForValue().get(username);
if (acct != null && acct.getStatus() == Status.EXPIRED) {
// route user to account renewal flow before login
} Type guard
boolean isActiveAccount(RedisUserAccount a) { return a != null && a.getStatus() == Status.OK; } Try / catch
try {
authHandler.authenticate(credential);
} catch (AccountExpiredException e) {
// account-expired messaging / renewal link
} Prevention
- Automate renewal -> status re-sync
- Alert on accounts approaching expiry
- Avoid long-lived Redis snapshots of expiry state
When it happens
Trigger: RedisUserAccount.getStatus() == Status.EXPIRED on the matched account after a successful password check.
Common situations: Password or account expiry date passed and a sync job marks status EXPIRED in Redis; expiry not extended after renewal; Redis holds a stale snapshot from before the account was renewed upstream.
Related errors
- AccountNotFoundException
- FailedLoginException
- AccountDisabledException
- Radius authentication failed for user
- Radius authentication failed for user
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/ea922d9f9d233ae9.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-redis-authentication/src/main/java/org/apereo/cas/redis/RedisAuthenticationHandler.java:45
super(name, principalFactory, order);
this.redisTemplate = redisTemplate;
}
@Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
final UsernamePasswordCredential credential,
final String originalPassword) throws Throwable {
val account = (RedisUserAccount) redisTemplate.opsForValue().get(credential.getUsername());
if (account == null) {
throw new AccountNotFoundException();
}
if (!getPasswordEncoder().matches(originalPassword, account.getPassword())) {
LOGGER.warn("Account password on record for [{}] does not match the given/encoded password", credential.getId());
throw new FailedLoginException();
}
switch (account.getStatus()) {
case DISABLED -> throw new AccountDisabledException();
case EXPIRED -> throw new AccountExpiredException();
case LOCKED -> throw new AccountLockedException();
case MUST_CHANGE_PASSWORD -> throw new AccountPasswordMustChangeException();
case OK -> LOGGER.debug("Account status is OK");
}
val principal = principalFactory.createPrincipal(account.getUsername(), account.getAttributes());
return createHandlerResult(credential, principal, new ArrayList<>());
}
}
View on GitHub (pinned to e7288fc434)