spring-projects/spring-security · error · BadCredentialsException
Bad credentials
Error message
Bad credentials
What it means
DaoAuthenticationProvider throws BadCredentialsException in additionalAuthenticationChecks when the authentication token carries no credentials at all (authentication.getCredentials() == null). Spring Security deliberately reuses a generic 'Bad credentials' message so attackers cannot distinguish a missing password from a wrong one. This is thrown after the user has been successfully looked up by username.
Source
Thrown at core/src/main/java/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java:85
private volatile @Nullable String userNotFoundEncodedPassword;
private final UserDetailsService userDetailsService;
private UserDetailsPasswordService userDetailsPasswordService = UserDetailsPasswordService.NOOP;
private @Nullable CompromisedPasswordChecker compromisedPasswordChecker;
public DaoAuthenticationProvider(UserDetailsService userDetailsService) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.userDetailsService = userDetailsService;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Failed to authenticate since no credentials provided");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.get().matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
@Override
protected void doAfterPropertiesSet() {
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
}
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {View on GitHub (pinned to 96852e8860)
Solutions
- Ensure the password is non-null when constructing UsernamePasswordAuthenticationToken(username, password) before calling AuthenticationManager.authenticate()
- If credentials are supplied via HTTP, verify the request actually carries the password parameter and that the filter reading it (e.g. UsernamePasswordAuthenticationFilter) has the correct parameter names configured
- When building tokens in tests or code, assert credentials are present: Assert.hasText(password, ...) before authenticate()
Example fix
// before Authentication auth = new UsernamePasswordAuthenticationToken(username, null); authManager.authenticate(auth); // after Assert.hasText(password, "password is required"); Authentication auth = new UsernamePasswordAuthenticationToken(username, password); authManager.authenticate(auth);
Defensive patterns
Strategy: validation
Validate before calling
if (password == null || password.isEmpty()) { throw new IllegalArgumentException("password is required"); }
Authentication token = new UsernamePasswordAuthenticationToken(username, password); Type guard
boolean hasCredentials(Authentication a) { return a != null && a.getCredentials() instanceof String s && !s.isEmpty(); } Try / catch
try { return authManager.authenticate(token); } catch (BadCredentialsException e) { throw new LoginFailureException("Invalid username or password"); } Prevention
- Always construct UsernamePasswordAuthenticationToken with a non-null password
- Bind login DTOs with @NotBlank on the password field and validate before authenticating
- Log a debug message on missing credentials server-side to catch misconfigured clients early
When it happens
Trigger: Calling UsernamePasswordAuthenticationToken(username, null) and passing it to AuthenticationManager.authenticate(); an AuthenticationProvider or filter upstream that strips or never sets credentials; custom filters building the token before the servlet request parameters are read (e.g. missing Content-Type so parameters are not populated).
Common situations: Custom REST login endpoints where the JSON body password field is absent or null; misconfigured form login where the password parameter name was changed without updating the filter; clients sending only a username in the auth request; tests constructing the token with a null credential.
Related errors
- RunAsImplAuthenticationProvider.incorrectKey
- CasAuthenticationProvider.incorrectKey
- Failed to authenticate the one-time token
- Bad credentials
- No pre-authenticated principal found in request.
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/fc81be4a137bc273.
Report an issue: GitHub.