Search code examples
spring-securitykeycloak

Spring Boot KeyCloak not invoking success handler


I am using Spring Boot KeyCloak in my application to connect with KeyCloak. However I have a custom success handler which is not being invoked. I am not sure why. here is my code:

SecurityConfiguration.java:

@KeycloakConfiguration
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Bean
    @Primary
    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
        filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
        filter.setAuthenticationSuccessHandler(successHandler());
        filter.setAuthenticationFailureHandler(failureHandler());
        return filter;
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf().disable().authorizeRequests()
            .antMatchers("/**").authenticated();
    }

    @NotNull
    @Bean
    public KeyCloakAuthSuccessHandler successHandler() {
        return new KeyCloakAuthSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
    }

    @NotNull
    @Bean
    public KeyCloakAuthFailureHandler failureHandler() {
        return new KeyCloakAuthFailureHandler();
    }

}

And in my KeyCloakAuthSuccessHandler.java, I have:

@Slf4j
public class KeyCloakAuthSuccessHandler extends KeycloakAuthenticationSuccessHandler {

    @Autowired
    ObjectMapper mapper;

    public KeyCloakAuthSuccessHandler(AuthenticationSuccessHandler fallback) {
        super(fallback);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        log.error("inside success handler");
        if (authentication.getPrincipal() instanceof KeycloakPrincipal) {
            AccessToken token = ((KeycloakPrincipal<?>) authentication.getPrincipal()).getKeycloakSecurityContext().getToken();
            // do other stuff
        }
    }
}

The above code doesn't invoked the success handler however a similar failure handler is working and getting invoked.


Solution

  • As discussed over the comments, this is because the success handler is not invoked during non-interactive(non-human ways - bearer/basic) login of Keycloak. If you want to invoke success handler each time irrespective of the way of login, write a custom KeycloakAuthencticationProcessingFilter by extending the same and change this line from the original one by over-riding it.

    A rough example will look like:

    public class CustomKeycloakAuthenticationProcessingFilter extends KeycloakAuthenticationProcessingFilter {
        
        public CustomKeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager) {
            super(authenticationManager);
        }
    
        public CustomKeycloakAuthenticationProcessingFilter(AuthenticationManager authenticationManager, RequestMatcher requiresAuthenticationRequestMatcher) {
            super(authenticationManager, requiresAuthenticationRequestMatcher);
        }
    
        @Override
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                                Authentication authResult) throws IOException, ServletException {
            // Line of importance down here
            if (authResult instanceof KeycloakAuthenticationToken) {
                super.successfulAuthentication(request, response, chain, authResult);
                return;
            }
            // whatever spring-boot-keycloak does copy paste here
        }
    }