spring-projects/spring-security · error · BadCredentialsException
Bad credentials
Error message
Bad credentials
What it means
LdapAuthenticationProvider.doAuthentication() catches UsernameNotFoundException from the authenticator and, when hideUserNotFoundExceptions is true (the default), masks it as BadCredentialsException('Bad credentials') to avoid revealing which usernames exist. When hideUserNotFoundExceptions is false the original UsernameNotFoundException is rethrown.
Source
Thrown at ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java:184
public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
}
@Override
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) {
try {
return getAuthenticator().authenticate(authentication);
}
catch (PasswordPolicyException ex) {
// The only reason a ppolicy exception can occur during a bind is that the
// account is locked.
throw new LockedException(
this.messages.getMessage(ex.getStatus().getErrorCode(), ex.getStatus().getDefaultMessage()));
}
catch (UsernameNotFoundException ex) {
if (this.hideUserNotFoundExceptions) {
throw new BadCredentialsException(
this.messages.getMessage("LdapAuthenticationProvider.badCredentials", "Bad credentials"));
}
throw ex;
}
catch (NamingException ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
@Override
protected Collection<? extends GrantedAuthority> loadUserAuthorities(DirContextOperations userData, String username,
String password) {
return getAuthoritiesPopulator().getGrantedAuthorities(userData, username);
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Verify userSearchBase and the search filter (e.g. (uid={0}) vs (sAMAccountName={0})) match the directory schema and actually locate the user (test with ldapsearch).
- Check the search uses the correct base DN and that the context source's manager credentials allow reading the user subtree.
- Remember 'Bad credentials' here can mean 'user not found' — don't debug only the password path.
- For debugging only, set hideUserNotFoundExceptions(false) temporarily to surface the real UsernameNotFoundException.
- Confirm users are synced/present in the directory the app points to (not a staging vs prod mismatch).
Example fix
// before
provider.setHideUserNotFoundExceptions(false); // leaks user existence in prod
// after
// prod: keep masking (default true)
provider.setHideUserNotFoundExceptions(true);
// fix lookup instead:
LdapUserSearch search = new FilterBasedLdapUserSearch("ou=people", "(sAMAccountName={0})", contextSource);
provider.setUserSearch(search); Defensive patterns
Strategy: try-catch
Validate before calling
// verify user resolves before authenticating
LdapTemplate ldap = new LdapTemplate(contextSource);
boolean exists = !ldap.search("ou=people", "(sAMAccountName={0})",
new String[] { username }, new AbstractContextMapper<Object>() {
protected Object doMapFromContext(DirContextOperations c) { return new Object(); }
}).isEmpty();
if (!exists) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid username or password"); Try / catch
try {
return authManager.authenticate(token);
} catch (BadCredentialsException e) {
// can mean wrong password OR unknown user (hidden) — do not distinguish to clients
logger.debug("ldap auth failed for {}", token.getName());
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid username or password");
} catch (UsernameNotFoundException e) {
// only reachable when hideUserNotFoundExceptions=false
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "invalid username or password");
} Prevention
- Validate userSearchBase/filter with ldapsearch; most 'Bad credentials' here are lookup misconfigurations.
- Keep hideUserNotFoundExceptions=true in production to prevent account enumeration.
- Temporarily set it false in dev to see the real UsernameNotFoundException.
- Confirm user provisioning/sync so expected accounts exist in the target directory.
When it happens
Trigger: The configured UserSearch/LdapUserSearch finds no directory entry matching the submitted username (or the bind DN lookup fails) and doAuthentication translates the resulting UsernameNotFoundException into this BadCredentialsException unless user-not-found exceptions are not hidden.
Common situations: Typo'd username, wrong userSearchBase or filter so existing users are never found, users in an OU not covered by the search, casing/base DN mismatch after a directory migration, or testing account-enumeration protection.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- Bad credentials
- Bad credentials
- Bad credentials
- RunAsImplAuthenticationProvider.incorrectKey
- CasAuthenticationProvider.incorrectKey
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/b46184bcd5516d18.
Report an issue: GitHub.