spring-projects/spring-security · error · BadCredentialsException
Bad credentials
Error message
Bad credentials
What it means
BindAuthenticator.authenticate() throws BadCredentialsException('Bad credentials') when every configured strategy — DN patterns and user search — failed to produce an authenticated user (bindWithDn/bindWithSearch returned null or the user could not be found). It is the generic LDAP 'wrong username or password' answer, deliberately vague.
Source
Thrown at ldap/src/main/java/org/springframework/security/ldap/authentication/BindAuthenticator.java:96
if (user != null) {
break;
}
}
if (user == null) {
logger.debug(LogMessage.of(() -> "Failed to bind with any user DNs " + getUserDns(username)));
}
// Otherwise use the configured search object to find the user and authenticate
// with the returned DN.
if (user == null && getUserSearch() != null) {
logger.trace("Searching for user using " + getUserSearch());
DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
user = bindWithDn(userFromSearch.getDn().toString(), username, password, userFromSearch.getAttributes());
if (user == null) {
logger.debug("Failed to find user using " + getUserSearch());
}
}
if (user == null) {
throw new BadCredentialsException(
this.messages.getMessage("BindAuthenticator.badCredentials", "Bad credentials"));
}
return user;
}
private @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password) {
return bindWithDn(userDnStr, username, password, null);
}
private @Nullable DirContextOperations bindWithDn(String userDnStr, String username, String password,
@Nullable Attributes attrs) {
BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource();
Name userDn = LdapUtils.newLdapName(userDnStr);
Name fullDn = LdapUtils.prepend(userDn, ctxSource.getBaseLdapName());
logger.trace(LogMessage.format("Attempting to bind as %s", fullDn));
DirContext ctx = null;
try {
ctx = getContextSource().getContext(fullDn.toString(), password);View on GitHub (pinned to 96852e8860)
Solutions
- Verify userDnPatterns / UserSearch base and filter resolve to the actual directory entry (test with ldapsearch).
- Confirm the user's account is enabled and the password is correct by binding manually with ldapwhoami.
- Check the context source's manager DN/password are valid — a failing search context makes lookups return nothing.
- Enable DEBUG logging for BindAuthenticator ('Failed to find user using ...') to see which strategy failed.
- Confirm URL/port of the LdapContextSource and that TLS (ldaps/StartTLS) matches the directory's requirements.
Example fix
// before
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=wrong,dc=example,dc=org"});
authManager.authenticate(token); // Bad credentials
// after
authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people,dc=example,dc=org"});
// or use search:
LdapUserSearch search = new FilterBasedLdapUserSearch("ou=people", "(uid={0})", contextSource);
authenticator.setUserSearch(search);
authManager.authenticate(token); Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check LDAP config at startup
Assert.hasText(contextSource.getBase(), "base DN must be set");
try (DirContext ctx = contextSource.getContext(managerDn, managerPassword)) {
ctx.search("ou=people", "(objectClass=person)", new SearchControls()); // search path works
} Try / catch
try {
return authManager.authenticate(token);
} catch (BadCredentialsException e) {
logger.debug("LDAP bind failed for user lookup");
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid username or password");
} Prevention
- Test userDnPatterns and user-search filters with ldapsearch before wiring them in.
- Verify manager DN/password; a broken search context makes all users 'not found'.
- Enable DEBUG logging on BindAuthenticator to see which strategy failed.
- Check account state (locked/expired) in the directory, not just the password.
When it happens
Trigger: All DN patterns fail to bind (wrong password or DN template doesn't match the entry), and the user search either finds no entry or its bind fails; after the loop, user remains null and this exception is thrown.
Common situations: UserSearchBase or userDnPatterns misconfigured so the entry is never found, account locked/disabled or password expired in the directory, password typo, or LDAP server refusing binds for the search principal so lookup silently fails.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/f6f71c5853178d75.
Report an issue: GitHub.