Description
Summary
When configured to use LDAP Authentication in it's default (bind verification instead of password verification) setup, attempting to log in with invalid credentials, such as a bad password, does not lead to an AuthenticationFailureEvent as one would expect.
I've traced this issue to its source, and you could also more technically summarize this problem by saying that BindAuthenticator#bindWithDn should consider javax.naming.AuthenticationException a BadCredentialException
Background
Spring Security's org.springframework.security.authentication.DefaultAuthenticationEventPublisher#publishAuthenticationFaulure method is set up to fire AuthenticationFailureBadCredentialsEvents when BadCredentialsExceptions are thrown (similar behavior exists for other authentication-related exceptions) during the actual authentication process. However, when configured to use LDAP Authentication, the BindAuthenticator doesn't appropriately handle LDAP Exceptions, which means InternalAuthenticationServiceExceptions are thrown instead of an appropriate exception. As this incorrect exceptions bubbles up, the DefaultAuthenticationPublisher#publishAuthenticationFailure method ignores the exception and doesn't fire any events. What should happen is that the BindAuthenticator#bindUsingDn method should catch and convert exceptions (in my case a javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
)
Actual Behavior
BindAuthenticator#bindWithDn does not catch/convert javax.naming.AuthenticationExceptions, which then instead causes the thrown exception to get wrapped as a org.springframework.security.authentication.InternalAuthenticationServiceException: [LDAP: error code 49 - Invalid Credentials]
, which does not cause the DefaultAuthenticationPublisher#publishAuthenticationFailure
to fire a AuthenticationFailureBadCredentialsEvent
Expected Behavior
BindAuthenticator#bindWithDn should catch/convert javax.naming.AuthenticationExceptions, such that the DefaultAuthenticationPublisher#publishAuthenticationFailure
will know to fire appropriate AuthenticationFailureEvent
s
Configuration
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns(env.getProperty("com.acme.userDnPattern"))
.groupSearchBase(env.getProperty("com.acme.groupSearchBase"))
.groupSearchFilter(env.getProperty("com.acme.groupSearchFilter"))
.contextSource(tlsSpringSecurityContextSource());
}
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
}
Version
Running with Spring Boot v1.2.5.RELEASE, Spring v4.1.7.RELEASE, Spring Security 4.0.2.RELEASE
I've inspected the current code involved in this issue, and it hasn't changed, so this is still an issue.