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

  1. Unlock the account in your user store (clear the locked flag / lock timestamp)
  2. If lockout is automatic, wait out the lock duration or provide an unlock/reset flow
  3. If you don't use account locking, return true from isAccountNonLocked() in your UserDetails
  4. 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

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/78cb35bbc9273be3. Report an issue: GitHub.