spring-projects/spring-security · error · LockedException
User account is locked
Error message
User account is locked
What it means
The DefaultPreAuthenticationChecks.check() in AbstractUserDetailsAuthenticationProvider throws LockedException 'User account is locked' when UserDetails.isAccountNonLocked() returns false. Authentication is rejected because the account is administratively or automatically locked.
Source
Thrown at core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java:373
* @param alwaysPerformAdditionalChecksOnUser
* @since 5.7.23
*/
public void setAlwaysPerformAdditionalChecksOnUser(boolean alwaysPerformAdditionalChecksOnUser) {
this.alwaysPerformAdditionalChecksOnUser = alwaysPerformAdditionalChecksOnUser;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
private class DefaultPreAuthenticationChecks implements UserDetailsChecker {
@Override
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {
AbstractUserDetailsAuthenticationProvider.this.logger
.debug("Failed to authenticate since user account is locked");
throw new LockedException(AbstractUserDetailsAuthenticationProvider.this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
}
if (!user.isEnabled()) {
AbstractUserDetailsAuthenticationProvider.this.logger
.debug("Failed to authenticate since user account is disabled");
throw new DisabledException(AbstractUserDetailsAuthenticationProvider.this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
}
if (!user.isAccountNonExpired()) {
AbstractUserDetailsAuthenticationProvider.this.logger
.debug("Failed to authenticate since user account has expired");
throw new AccountExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
}
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Unlock the account in your user store (clear the locked flag / lock timestamp)
- If lockout is automatic, wait out the lock duration or provide an unlock/reset flow
- If you don't use account locking, return true from isAccountNonLocked() in your UserDetails
- Catch LockedException in the failure handler and tell the user why login failed and how to unlock
Example fix
// before UPDATE users SET locked = true WHERE username = ?; // after UPDATE users SET locked = false, failed_attempts = 0 WHERE username = ?;
Defensive patterns
Strategy: try-catch
Validate before calling
UserDetails user = uds.loadUserByUsername(username);
if (!user.isAccountNonLocked()) { throw new IllegalStateException("Account locked: " + username); } Type guard
boolean isLoginAllowed(UserDetails u) { return u.isAccountNonLocked(); } Try / catch
try { authMgr.authenticate(token); } catch (LockedException e) { return ResponseEntity.status(423).body("Account locked. Unlock or wait for lockout to expire."); } Prevention
- Implement lockout with an explicit unlock mechanism and clear messaging
- Reset failed-attempt counters on successful login
- If locking is unused, always return true from isAccountNonLocked()
When it happens
Trigger: DaoAuthenticationProvider post-load validation of a UserDetails whose isAccountNonLocked() returns false.
Common situations: Too many failed login attempts triggering an account-lockout service (e.g. Spring Security's own lockout implementations or a DB lock_flag); admin-locked accounts; UserDetails returning isAccountNonLocked=false by mistake.
Related errors
- RunAsImplAuthenticationProvider.incorrectKey
- Authenticated principal required to operate with ACLs
- CasAuthenticationProvider.incorrectKey
- oidc_provider_not_configured
- Did you forget to add a global <authentication-manager> elem
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/78cb35bbc9273be3.
Report an issue: GitHub.