spring-projects/spring-security · error · LockedException
AbstractUserDetailsAuthenticationProvider.locked
AbstractUserDetailsAuthenticationProvider.locked
Error message
User account is locked
What it means
AbstractUserDetailsReactiveAuthenticationManager.defaultPreAuthenticationChecks runs before password verification in reactive authentication. If UserDetails.isAccountNonLocked() returns false, it throws LockedException with message 'User account is locked' (message code AbstractUserDetailsAuthenticationProvider.locked). Authentication is aborted regardless of credentials.
Source
Thrown at core/src/main/java/org/springframework/security/authentication/AbstractUserDetailsReactiveAuthenticationManager.java:77
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
private ReactiveUserDetailsPasswordService userDetailsPasswordService = ReactiveUserDetailsPasswordService.NOOP;
private Scheduler scheduler = Schedulers.boundedElastic();
private UserDetailsChecker preAuthenticationChecks = this::defaultPreAuthenticationChecks;
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(View on GitHub (pinned to 96852e8860)
Solutions
- Return true from isAccountNonLocked() in your UserDetails implementation (or fix the account_non_locked flag in the user store)
- Unlock the account in your user database / admin tooling
- Catch LockedException in the reactive pipeline and surface a clear account-locked message to the client
- If you want to skip these checks, supply a custom UserDetailsChecker via setPreAuthenticationChecks that omits the lock check
Example fix
// before
@Override
public boolean isAccountNonLocked() { return locked; } // locked flag stuck false
// after
@Override
public boolean isAccountNonLocked() { return !accountLockedByAdmin(); } Defensive patterns
Strategy: try-catch
Validate before calling
if (!userDetails.isAccountNonLocked()) throw new LockedException("Account locked: " + username); Type guard
boolean authenticatable(UserDetails u) { return u.isAccountNonLocked() && u.isEnabled() && u.isAccountNonExpired(); } Try / catch
authManager.authenticate(token)
.onErrorResume(LockedException.class, e -> Mono.error(new AuthFailure("ACCOUNT_LOCKED"))); Prevention
- Always override all UserDetails status methods; defaults are false
- Sync account_non_locked flags with lockout policy state
- Admin UI/tests to verify lock state before login attempts
- Map LockedException to a clear API error code for clients
When it happens
Trigger: Authenticating (authenticate(Mono<Authentication>)) a UserDetails whose isAccountNonLocked() returns false — typically a custom UserDetails implementation, or a JdbcUserDetailsManager users table row with enabled/non_locked flag set to locked.
Common situations: Accounts locked by brute-force lockout policies; users table schema where account_non_locked column defaults to 0/false; custom UserDetails beans forgetting to override isAccountNonLocked() (default false on UserDetails itself).
Related errors
- AbstractUserDetailsAuthenticationProvider.disabled
- AbstractUserDetailsAuthenticationProvider.expired
- AccountStatusUserDetailsChecker.locked
- AbstractUserDetailsAuthenticationProvider.credentialsExpired
- User account is locked
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/56c27f628f209300.
Report an issue: GitHub.