'AuthenticationSuccessEvent never fired

I writing app where user logs in using facebook.

My security config/application class:

@SpringBootApplication
@EnableOAuth2Sso
@ComponentScan(basePackages = { "app" })
public class Application extends WebSecurityConfigurerAdapter {

    @SuppressWarnings("SpringJavaAutowiringInspection")
    @Autowired
    private OAuth2ClientContext oauth2ClientContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/**")
                .authorizeRequests()
                .antMatchers("/",
                        "/login**",
                        "/webjars/**",
                        "/bower_components/**",
                        "/assets/**",
                        "/app/**",
                        "/api/auth/isAuthenticated")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and().formLogin().defaultSuccessUrl("/", true).loginPage("/login").permitAll()
                .and().logout().logoutSuccessUrl("/").permitAll()
                .and().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
    }

    @Bean
    @ConfigurationProperties("facebook")
    ClientResources facebook() {
        return new ClientResources();
    }

    private Filter ssoFilter() {
        CompositeFilter filter = new CompositeFilter();
        List<Filter> filters = new ArrayList<>();
        filters.add(ssoFilter(facebook(), "/login/facebook"));
        filter.setFilters(filters);
        return filter;
    }

    private Filter ssoFilter(ClientResources client, String path) {
        OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(path);
        OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
        filter.setRestTemplate(facebookTemplate);
        filter.setTokenServices(new UserInfoTokenServices(client.getResource().getUserInfoUri(), client.getClient().getClientId()));
        return filter;
    }

    class ClientResources {
        private OAuth2ProtectedResourceDetails client = new AuthorizationCodeResourceDetails();
        private ResourceServerProperties resource = new ResourceServerProperties();

        public OAuth2ProtectedResourceDetails getClient() {
            return client;
        }

        public ResourceServerProperties getResource() {
            return resource;
        }
    }

My problem is that even that I configured listener:

package app;
    @Component
    public class AuthenticationListener implements ApplicationListener<AuthenticationSuccessEvent> {

        @Override
        public void onApplicationEvent(AuthenticationSuccessEvent event) {
            System.out.println("Event fired");
        }
    }

it is never fired. I tried solution provided here : Spring boot OAuth successful login listener not triggering but it does not help either. SecurityContextHolder.getContext().getAuthentication().isAuthenticated() returns true after logging in.



Solution 1:[1]

OAuth SSO support does not fire AuthenticationSuccessEvent indeed. This is something I've been facing recently as well. This is implemented now but not released yet.

Solution 2:[2]

To publish authentication events it's possible to override authenticationHandler's.

basic security config:

@EnableWebFluxSecurity
public class SecurityConfig {

    @Autowired
    public ApplicationEventPublisher applicationEventPublisher;

    @Bean
    public AuthenticationEventPublisher authenticationEventPublisher() {
        return new DefaultAuthenticationEventPublisher(applicationEventPublisher);
    }

    @Bean
    public ServerAuthenticationEntryPoint serverAuthenticationEntryPoint() {
        return new HttpBasicServerAuthenticationEntryPoint();
    }
    
    @Bean
    public LoggerListener loggerListener() {
        return new LoggerListener();
    }

    @Bean
    public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {

        return http.authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .formLogin(login -> {
                    login.authenticationSuccessHandler(new LoggingAuthenticationSuccessHandler());
                    login.authenticationFailureHandler(new LoggingAuthenticaionFailedHandler(serverAuthenticationEntryPoint()));
                })
                .build();
    }
}

success handler:

class LoggingAuthenticationSuccessHandler extends WebFilterChainServerAuthenticationSuccessHandler {

    @Autowired
    private AuthenticationEventPublisher eventPublisher;


    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
        this.eventPublisher.publishAuthenticationSuccess(authentication);
        return super.onAuthenticationSuccess(webFilterExchange,authentication);
    }
}

failure handler:

class LoggingAuthenticaionFailureHandler extends ServerAuthenticationEntryPointFailureHandler  {

        @Autowired
        private AuthenticationEventPublisher eventPublisher;

        public LoggingAuthenticaionFailureHandler(ServerAuthenticationEntryPoint authenticationEntryPoint) {
            super(authenticationEntryPoint);
        }

        @Override
        public Mono<Void> onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
            AnonymousAuthenticationToken token = new AnonymousAuthenticationToken("key", "anonymousUser",
                    AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
            this.eventPublisher.publishAuthenticationFailure(exception, token);
            return super.onAuthenticationFailure(webFilterExchange, exception);
        }
    }

and event listener:

public class AuthenticationEvents {

    private LoggerListener loggerListener;

    @Autowired
    public AuthenticationEvents(LoggerListener logger) {
        this.loggerListener = logger;
    }

    @EventListener
    public void onSuccess(InteractiveAuthenticationSuccessEvent success) {
        loggerListener.onApplicationEvent(success);
    }

    @EventListener
    public void onSuccess(AuthenticationSuccessEvent success) {
        loggerListener.onApplicationEvent(success);
    }

    @EventListener
    public void onFailure(AbstractAuthenticationFailureEvent failures) {
        loggerListener.onApplicationEvent(failures);
    }
}

The standard LoggerListener could be replaced with own implementation. Keep in mind, if Your security filter chain has UsernamePasswordAuthenticationFilter then overrides the success handler is not necessary.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Stephane Nicoll
Solution 2 cane