spring-projects/spring-security · error · AccountExpiredException
AbstractUserDetailsAuthenticationProvider.expired
AbstractUserDetailsAuthenticationProvider.expired
Error message
User account has expired
What it means
defaultPreAuthenticationChecks throws AccountExpiredException with 'User account has expired' (message code AbstractUserDetailsAuthenticationProvider.expired) when UserDetails.isAccountNonExpired() returns false. This is a pre-authentication check in the reactive authentication manager; the account's validity period has ended.
Source
Thrown at core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java:87
private UserDetailsChecker postAuthenticationChecks = this::defaultPostAuthenticationChecks;
private @Nullable ReactiveCompromisedPasswordChecker compromisedPasswordChecker;
private void defaultPreAuthenticationChecks(UserDetails user) {
if (!user.isAccountNonLocked()) {
this.logger.debug("User account is locked");
throw new LockedException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",
"User account is locked"));
}
if (!user.isEnabled()) {
this.logger.debug("User account is disabled");
throw new DisabledException(
this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
}
if (!user.isAccountNonExpired()) {
this.logger.debug("User account is expired");
throw new AccountExpiredException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
}
}
private void defaultPostAuthenticationChecks(UserDetails user) {
if (!user.isCredentialsNonExpired()) {
this.logger.debug("User account credentials have expired");
throw new CredentialsExpiredException(this.messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
}
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
String username = authentication.getName();
String presentedPassword = (authentication.getCredentials() != null)
? authentication.getCredentials().toString() : null;
// @formatter:offView on GitHub (pinned to 96852e8860)
Solutions
- Return true from isAccountNonExpired() (or extend the expiry date in the user store) for accounts that should remain valid
- Implement a renewal process updating the account expiry timestamp
- Catch AccountExpiredException and prompt the user to renew/reactivate
- Check your UserDetails overrides all four status methods; interface defaults are all false
Example fix
// before
@Override public boolean isAccountNonExpired() { return false; }
// after
@Override public boolean isAccountNonExpired() { return expiryDate.isAfter(Instant.now()); } Defensive patterns
Strategy: try-catch
Validate before calling
if (!userDetails.isAccountNonExpired()) throw new AccountExpiredException("Account expired: " + username); Type guard
boolean accountCurrent(UserDetails u) { return u.isAccountNonExpired(); } Try / catch
authManager.authenticate(token)
.onErrorResume(AccountExpiredException.class, e -> Mono.error(new AuthFailure("ACCOUNT_EXPIRED"))); Prevention
- Compute account expiry from a real date field, not a constant false
- Add renewal/extension workflows for time-limited accounts
- Monitor expiry dates approaching to warn users
- Override all UserDetails methods; interface defaults return false
When it happens
Trigger: authenticate() on a UserDetails whose isAccountNonExpired() returns false — typically accounts with an expiry date in the past (e.g. temporary/contract accounts) or custom UserDetails not overriding isAccountNonExpired() (default false).
Common situations: Time-limited accounts (contractors, trials) passing their expiry date; custom User implementations omitting isAccountNonExpired(); test fixtures with hardcoded expiry timestamps now in the past.
Related errors
- AbstractUserDetailsAuthenticationProvider.locked
- AbstractUserDetailsAuthenticationProvider.disabled
- AbstractUserDetailsAuthenticationProvider.credentialsExpired
- AccountStatusUserDetailsChecker.locked
- AccountStatusUserDetailsChecker.expired
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/c02e50a3e6090f1a.
Report an issue: GitHub.