Skip to content

HttpSecurity Bean does not set DefaultAuthenticationEventPublisher #11449

@andrecampanini

Description

@andrecampanini

Context
The purpose of my module/starter is to be used by other applications in the company I work in order to publish automatically specific logs related to authentication and authorization via ApplicationListeners using Spring Security's and OAuth2's strategy for publishing authentication events (DefaultAuthenticationEventPublisher).

Expected behaviour: using old WebSecurityConfigurerAdapter
My old security config code based on Spring Boot 2.6 worked perfectly fine:

@Configuration @EnableWebSecurity
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                . . . 
            .and()
                .oauth2ResourceServer()
                    .jwt();
    }
}

Description of the bug
After upgrading to Spring Boot 2.7 and replacing the usage of deprecated WebSecurityConfigurerAdapter class in favour of a config method @Bean that returns SecurityFilterChain as recommended in this article and release notes my applications don't publish automatically anymore because they do not have a valid AuthenticationEventPublisher as before.

How to reproduce it

@Configuration @EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                . . . 
            .and()
                .oauth2ResourceServer()
                    .jwt();
        return http.build();
    }
}

Cause of the bug
The reason for this problem is: the object BearerTokenAuthenticationFilter uses an instance of ProviderManager as AuthenticationManager (so same way when using WebSecurityConfigurerAdapter). But as default an instance of ProviderManager declares its AuthenticationEventPublisher this way:

from: https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/authentication/ProviderManager.java

public class ProviderManager implements AuthenticationManager, . . . {

    . . .

    private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();

More details about the bug
So this is the problem: NullEventPublisher is a useless implementation which doesn't publish events.

Why this problem just now? When using WebSecurityConfigurerAdapter - this class was setting an instance of DefaultAuthenticationEventPublisher to ProviderManager's eventPublisher attribute over its default value NullEventPublisher's instance.

Note: I also tried using Spring Security autoconfiguration which is org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration#authenticationEventPublisher() from spring-boot-autoconfigure - but this one is never injected into ProviderManager.

Expected behaviour via a workaround - but I'm not fine with it
After some tests I was able to "fix the problem" with the following code:

@Configuration
@ConditionalOnClass({AuthenticationEventPublisher.class, JwtAuthenticationProvider.class})
 public class SpringConfiguration { //global configuration for several other applications using my module/starter
    @Bean
    public AuthenticationEventPublisher eventPublisher(ApplicationEventPublisher application) {
	    DefaultAuthenticationEventPublisher authentication = 
            new DefaultAuthenticationEventPublisher(application);
	
	    authentication.setDefaultAuthenticationFailureEvent(AuthenticationFailureBadCredentialsEvent.class);
	
        return authentication;
    }

    @Bean
    public ProviderManager providerManagerAvecDefaultAuthenticationPublisher(@Lazy JwtDecoder jwtDecoder, AuthenticationEventPublisher authenticationPublisher) {
        JwtAuthenticationProvider authenticationProvider = new JwtAuthenticationProvider(jwtDecoder);
        ProviderManager providerManager = new ProviderManager(Arrays.asList(authenticationProvider));
        providerManager.setAuthenticationEventPublisher(authenticationPublisher);
        return providerManager;
    }
}

And also had to adjust my security configuration:

@Configuration @EnableWebSecurity
public class ResourceServerConfig {

    @Autowired ProviderManager manager; //1

    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                . . . 
            .and()
                .oauth2ResourceServer()
                    .jwt()
                    .authenticationManager(manager); //2
        return http.build();
    }
}

But I have some big concerns:

  1. As my module/application is to be used by other applications - this "workaround" solution will force dozens of applications to add the lines followed by comments 1 and 2

  2. By adding a custom ProviderManager I am not aware of the risks of "forcing" a pre-built one for those applications
    So finally my question here is: Is there a way to bypass eventPublisher = new NullEventPublisher() from ProviderManager without forcing to configure oauth2ResourceServer().authenticationManager(manager) in all applications configuring its SecurityFilterChain?

  3. I had to use @Lazy for the injection of JwtDecoder - and also I'm not aware how this will affect different other applications

Metadata

Metadata

Labels

in: configAn issue in spring-security-configstatus: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions