Marcin Majewski
Marcin Majewski

Reputation: 1047

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.

Upvotes: 1

Views: 1629

Answers (2)

cane
cane

Reputation: 1027

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.

Upvotes: 0

Stephane Nicoll
Stephane Nicoll

Reputation: 33121

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.

Upvotes: 2

Related Questions