spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

SecurityFilterChain picks up wrong Authentication Provider

Open pavankjadda opened this issue 3 years ago • 3 comments

Describe the bug In my project we have 2 SecurityFilterChains

  1. externalFilterChain for external API requests
  2. defaultlFilterChain for Angular/React client applications

We use LDAP and JDBC authentication. Both activeDirectoryLdapAuthenticationProvider and daoAuthenticationProvider injected as beans. But externalFilterChain picks up daoAuthenticationProvider even though I specifically said it to use activeDirectoryLdapAuthenticationProvider

To Reproduce

External API FilterChain:

@Bean
public SecurityFilterChain externalFilterChain(HttpSecurity http) throws Exception {
	return http.antMatcher("/api/v1/external/search/**")
		.httpBasic(basic -> {})
		.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
		.authenticationProvider(activeDirectoryLdapAuthenticationProvider)
		.build();
}

Default FilterChain:

@Bean
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
	return http
		.httpBasic(basic -> {})
		.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
		.authenticationProvider(activeDirectoryLdapAuthenticationProvider())
		.authenticationProvider(daoAuthenticationProvider())
		.build();
}

AuthenticationProvider Beans:

    @Bean
    public CustomDaoAuthenticationProvider getDaoAuthenticationProvider() {
        CustomDaoAuthenticationProvider daoAuthenticationProvider = new CustomDaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(presDbUserDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }


   @Bean
    public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider =
                new ActiveDirectoryLdapAuthenticationProvider(ldapProperties.getDomain(), ldapProperties.getUrl());
        activeDirectoryLdapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
        activeDirectoryLdapAuthenticationProvider.setUserDetailsContextMapper(new UserDetailsContextMapper() {
   ..........     
})

        return activeDirectoryLdapAuthenticationProvider;
    }

Expected behavior User should be authenticated with provided Authentication Provider

Reports that include a sample will take priority over reports that do not. At times, we may require a sample, so it is good to try and include a sample up front.

pavankjadda avatar Jul 20 '22 15:07 pavankjadda

Could be related #10005

pavankjadda avatar Jul 20 '22 15:07 pavankjadda

@pavankjadda, when I copy the beans into my IDE, they don't compile due to referring to several other components in your application.

Will you please post a minimal sample? The best way is to share a GitHub repo that has only the necessary components to reproduce the issue.

jzheaux avatar Jul 20 '22 22:07 jzheaux

I created new repository that reproduces the issue. Make sure replace the AD config based on your environment.

pavankjadda avatar Jul 22 '22 21:07 pavankjadda

Sorry for the delay on this ticket, @pavankjadda.

I believe this is because HttpSecurity will pick up all authentication providers from beans as well as the DSL and consolidate them.

What I'd recommend instead is to formulate two AuthenticationManagers and set them on your DSLs like so:

@Bean
public SecurityFilterChain externalFilterChain(HttpSecurity http, 
        ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider) throws Exception {

        ProviderManager manager = new ProviderManager(activeDirectoryLdapAuthenticationProvider);

	return http.antMatcher("/api/v1/external/search/**")
		.httpBasic(basic -> {})
		.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
		.authenticationManager(manager)
		.build();
}

@Bean
public SecurityFilterChain defaultFilterChain(HttpSecurity http,
        ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider,
        DaoAuthenticationProvider daoAuthenticationProvider) throws Exception {

        ProviderManager manager = new ProviderManager(
                activeDirectoryLdapAuthenticationProvider, daoAuthenticationProvider);

	return http
		.httpBasic(basic -> {})
		.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
		.authenticationManager(manager)
		.build();
}

Because there is only one component for the DSL to decide on in this case, the precedence rules are a bit easier to manage with this arrangement.

jzheaux avatar Jan 07 '23 00:01 jzheaux