spring-projects/spring-security · error · BadCredentialsException
Bad credentials
Error message
Bad credentials
What it means
Spring Security LDAP's PasswordComparisonAuthenticator throws BadCredentialsException("Bad credentials") in authenticate() when the user was found via DN/search but the password comparison failed. Either the password attribute value did not match via the configured PasswordEncoder, or the LDAP bind-based comparison was rejected. It deliberately uses the generic message to avoid revealing whether the username exists.
Source
Thrown at ldap/src/main/java/org/springframework/security/ldap/authentication/PasswordComparisonAuthenticator.java:125
if (user == null) {
throw UsernameNotFoundException.fromUsername(username);
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Comparing password attribute '%s' for user '%s'",
this.passwordAttributeName, user.getDn()));
}
if (this.usePasswordAttrCompare && isPasswordAttrCompare(user, password)) {
logger.debug(LogMessage.format("Locally matched password attribute '%s' for user '%s'",
this.passwordAttributeName, user.getDn()));
return user;
}
Assert.notNull(password, "LDAP password cannot be null");
if (isLdapPasswordCompare(user, ldapTemplate, password)) {
logger.debug(LogMessage.format("LDAP-matched password attribute '%s' for user '%s'",
this.passwordAttributeName, user.getDn()));
return user;
}
throw new BadCredentialsException(
this.messages.getMessage("PasswordComparisonAuthenticator.badCredentials", "Bad credentials"));
}
private boolean isPasswordAttrCompare(DirContextOperations user, @Nullable String password) {
String passwordAttrValue = getPassword(user);
return this.passwordEncoder.matches(password, passwordAttrValue);
}
private @Nullable String getPassword(DirContextOperations user) {
Object passwordAttrValue = user.getObjectAttribute(this.passwordAttributeName);
if (passwordAttrValue == null) {
return null;
}
if (passwordAttrValue instanceof byte[]) {
return new String((byte[]) passwordAttrValue);
}
return String.valueOf(passwordAttrValue);
}View on GitHub (pinned to 96852e8860)
Solutions
- Verify the user is supplying the correct password (test an ldapwhoami/bind directly with the same DN and password).
- Check that the configured PasswordEncoder matches the hash format stored in the password attribute (e.g. LdapShaPasswordEncoder/SSHA vs BCrypt).
- Ensure the bind credentials used by the ContextSource have read access to the password attribute if using attribute comparison instead of bind comparison.
- Confirm searchFilter/base configuration returns the correct user entry and DN.
Example fix
// before: encoder does not match LDAP storage this.encoder = new BCryptPasswordEncoder(); // after: use the encoder matching the LDAP password hash scheme, or compare via bind authenticator.setPasswordCompare(new LdapShaPasswordEncoder()); // or configure the provider to authenticate by LDAP bind instead of attribute comparison
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: bind test before authentication
DirContext ctx = null;
try {
ctx = contextSource.getContext(username, rawPassword); // will throw NamingException on bad bind
} finally {
if (ctx != null) LdapUtils.closeContext(ctx);
} Try / catch
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (BadCredentialsException e) {
// generic message on purpose: show 'Invalid username or password'
} Prevention
- Match the PasswordEncoder to the LDAP password attribute hash format (SSHA/SHA vs BCrypt).
- Verify bind DN can read the password attribute when using attribute comparison.
- Test credentials directly with ldapwhoami before blaming the application.
- Never log the password or the exception details containing DNs.
When it happens
Trigger: Calling LdapAuthenticationProvider/PasswordComparisonAuthenticator.authenticate() when isLdapPasswordCompare() returns false — i.e. the supplied password does not match the user's password attribute (passwordAttributeName, default 'userPassword') per the configured PasswordEncoder, or the one-level bind comparison against the LDAP server fails.
Common situations: Wrong password entered by the user; PasswordEncoder mismatch (e.g. LDAP stores SSHA hashes but a plaintext or BCrypt encoder is configured); the LDAP bind comparison fails because the bind DN lacks permission to read the password attribute; typo in user DN/search base so the entry found is not the real user.
Related errors
- RunAsImplAuthenticationProvider.incorrectKey
- CasAuthenticationProvider.incorrectKey
- Bad credentials
- Failed to authenticate the one-time token
- Bad credentials
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/4e72b8a9b3659879.
Report an issue: GitHub.