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

  1. Verify userDnPatterns / UserSearch base and filter resolve to the actual directory entry (test with ldapsearch).
  2. Confirm the user's account is enabled and the password is correct by binding manually with ldapwhoami.
  3. Check the context source's manager DN/password are valid — a failing search context makes lookups return nothing.
  4. Enable DEBUG logging for BindAuthenticator ('Failed to find user using ...') to see which strategy failed.
  5. 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

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.