Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DaoAuthenticationProvider is autoconfigured when more than one AuthenticationProvider is registered #10005

Open
gbaso opened this issue Jun 25, 2021 · 6 comments
Labels
in: config An issue in spring-security-config status: feedback-provided Feedback has been provided type: bug A general bug

Comments

@gbaso
Copy link
Contributor

gbaso commented Jun 25, 2021

Describe the bug
By default, the AuthenticationManagerBuilderis autoconfigured with an AuthenticationProvider, if registered, or with a DaoAuthenticationProvider, if an UserDetailsService is registered. Both configurer back off if the AuthenticationManagerBuilder is already configured, i.e. if at least one AuthenticationProvider has been specified.

In particular, if a user registers their own AuthenticationProvider bean, it is added to the manager's providers list by InitializeAuthenticationProviderManagerConfigurer, and InitializeUserDetailsManagerConfigurer does nothing since it consider the manager already configured.

However, InitializeAuthenticationProviderManagerConfigurer only add the provider if there's just a single registered one. If multiple beans are registered, the configurer does nothing, but because the manager's providers list is still empty InitializeUserDetailsManagerConfigurer thinks that the manager was not yet configured, and creates the DaoAuthenticationProvider.

This is a problem if I want both a DaoAuthenticationProvider to handle UsernamePasswordAuthenticationToken and a second provider to handle SSO tokens. If I only register the second provider, SSO works but the DaoAuthenticationProvider is not present in the ProviderManager's providers list, so I cannot log in with username/password. If I also register a DaoAuthenticationProvider, the default one also will be created, possibly with a different configuration (e.g. a different PasswordEncoder).

The workaround I found is to register my SSO provider:

@Bean
public AuthenticationProvider ssoProvider(AuthenticationManagerBuilder auth) {
    var provider = new MySsoProvider();
    auth.authenticationProvider(provider);
    return provider;
}

and a dummy one:

@Bean
public AuthenticationProvider dummyAuthProvider() {
    return new AuthenticationProvider() {
        public boolean supports(Class<?> authentication) {
            return false;
        }
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            return null;
        }
    };
}

so I can use my SSO provider and the Dao provider configured by InitializeUserDetailsManagerConfigurer, but this is clearly not the intended way to do it.

To Reproduce
Register a UserDetailsService and multiple AuthenticationProviders in a @Configuration.

Expected behavior
InitializeUserDetailsManagerConfigurer should behave the same if either one or more than one AuthenticationProvider beans are registered.

Sample

spring-security-dao-configurer

@gbaso gbaso added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Jun 25, 2021
@gbaso
Copy link
Contributor Author

gbaso commented Sep 27, 2021

Any progress on this?

@gbaso
Copy link
Contributor Author

gbaso commented Jan 11, 2022

Issue is still present in latest version 5.6.1

@eleftherias
Copy link
Contributor

Thanks for reaching out @gbaso.

The class that initializes the authentication based on the AuthenticationProvider beans is InitializeAuthenticationProviderBeanManagerConfigurer.
From the Javadoc:

Lazily initializes the global authentication with an AuthenticationProvider if it is not yet configured and there is only a single Bean of that type.

To register multiple AuthenticationProviders in a the same AuthenticationManager you can use HttpSecurity#authenticationProvider.

For example:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
	return http
		.httpBasic(basic -> {})
		.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
		.authenticationProvider(new FirstProvider())
		.authenticationProvider(new SecondProvider())
		.build();
}

Feel free to give this a try and let us know if you have any trouble.

@eleftherias eleftherias added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Mar 31, 2022
@eleftherias eleftherias assigned eleftherias and unassigned rwinch Mar 31, 2022
@gbaso
Copy link
Contributor Author

gbaso commented Apr 6, 2022

Thanks @eleftherias, your suggested approach works fine.

However, it still feels weird to me that DaoAuthenticationProvider is autoconfigured when multiple beans of type AuthenticationProvider are registered.

The autoconfiguration for DaoAuthenticationProvider (correctly) backs off when a user register their own AuthenticationProvider, should it not do the same when there are multiple beans registered?
Or, iIf registering multiple AuthenticationProviders is not supported, maybe it could be better to throw an exception and advise the user on the correct approach.

What do you think?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 6, 2022
@rwinch rwinch assigned rwinch and unassigned eleftherias May 5, 2022
@rwinch rwinch removed their assignment Jun 6, 2022
@rwinch rwinch added in: config An issue in spring-security-config type: bug A general bug labels Jun 6, 2022
@UtkarshBhavsar
Copy link

I still have been facing the same issue with 5.7.3 version. I am implementing multiple securityfilterchain with different authentication provider. Do we have any update on this?

@RazvanSebastian
Copy link

RazvanSebastian commented Sep 6, 2023

Also on new 6.x versions, the behavbiour is the same.

I did some debug and I found that how HttpSecurity bean is firstly initialized on HttpSecurityConfiguration .

The proccess is starting here where the authenticationBuilder.parentAuthenticationManager(authenticationManager()); is called and then method getAuthenticationManager from AuthenticationConfiguration is called. Baiscally this one is initialzing the AuthenticationManagerBuilder.

If you take a look closer here, there is a lit of 3 GlobalAuthenticationConfigurerAdapter objects which are defined as beans and are used to initialize our AuthenticationManagerBuilder.

These 3 objects of type EnableGlobalAuthenticationAutowiredConfigurer, InitializeUserDetailsBeanManagerConfigurer, InitializeAuthenticationProviderBeanManagerConfigurer are used and

InitializeAuthenticationProviderBeanManagerConfigurer : this one is dealing with the initialization with AuthenticationProvider beans, BUT only if there is ONLY ONE bean (link here).

Since we have the situation where we have more than one AuthenticationProvider bean or none beans of this type, then InitializeUserDetailsBeanManagerConfigurer will be used and only that DaoAuthenticationProvider will be used (link here)

It seems that even if you register Java objects with authenticationProvider method, we are only setting the AuthenticationProviderBuilder and not AuthenticationManager so for example

http..authenticationProvider(someProvider).addFilterBefore(authenticationFilter(authenticationManager)

we are receiving on filter the AuthenticationManager where we have only DaoAuthenticationProvider

The solution for me is the following:

  1. Create a bean of type AuthenticationManager
   @Bean
    public AuthenticationManager authenticationManager(HttpSecurity http, AuthenticationProvider authenticationProvider1, AuthenticationProvider authenticationProvider2) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);

        authenticationManagerBuilder.authenticationProvider(authenticationProvider1);
        authenticationManagerBuilder.authenticationProvider(authenticationProvider2);

        return authenticationManagerBuilder.build();
    }
  1. This bean will be used to set directly on HttpSecurity builder the AuthenticationManager.
     @Bean
     public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authenticationManager) {
           // Other building methods
           http.authenticationManager(authenticationManager);
   }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config status: feedback-provided Feedback has been provided type: bug A general bug
Projects
None yet
Development

No branches or pull requests

6 participants