manjosh
manjosh

Reputation: 734

How to logout from Saml IDP when I logout from my application

I am new to SAML security and KEYCLOAK. I have a client in KEYCLOAK with SAML protocol. My application is configured to this SAML client. When I log in to my application, the authentication is successful at SAML and I am able to log into my application. User session is also created in keycloak. While performing logout operation, the user is just logged out from my application and not from SAML.The user remains active.

How do I perform a logout so that saml session is also cleared. I found that spring saml supports "/saml/logout" to clear the session. But this url needs to be explicitly called from the browser and then again a logout has to be performed from my application. Is there a way to perform both these calls in one shot.

below is my samlSecurityconfig java code:

 @Bean
public FilterChainProxy samlFilter() throws Exception {
    List<SecurityFilterChain> chains = new ArrayList<>();
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
            samlEntryPoint()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
            samlLogoutFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
            metadataDisplayFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
            samlWebSSOProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
            samlWebSSOHoKProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
            samlLogoutProcessingFilter()));
    return new FilterChainProxy(chains);
}

  // Handler for successful logout
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
    SimpleUrlLogoutSuccessHandler successLogoutHandler = new CustomSimpleUrlLogoutSuccessHandler();
    successLogoutHandler.setDefaultTargetUrl(config().getSp().getEntityBaseURL());
    return successLogoutHandler;
}

// Logout handler terminating local session
@Bean
public SecurityContextLogoutHandler logoutHandler() {
    SecurityContextLogoutHandler logoutHandler =
            new SecurityContextLogoutHandler();
    logoutHandler.setInvalidateHttpSession(true);
    logoutHandler.setClearAuthentication(true);
    return logoutHandler;
}

// Filter processing incoming logout messages
// First argument determines URL user will be redirected to after successful
// global logout
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
    return new SAMLLogoutProcessingFilter(successLogoutHandler(),
            logoutHandler());
}

// Overrides default logout processing filter with the one processing SAML
// messages
@Bean
public SAMLLogoutFilter samlLogoutFilter() {
    return new SAMLLogoutFilter(successLogoutHandler(),
            new LogoutHandler[]{logoutHandler()},
            new LogoutHandler[]{logoutHandler()});
}
@Bean
public FilterChainProxy samlFilter() throws Exception {
    List<SecurityFilterChain> chains = new ArrayList<>();
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
            samlEntryPoint()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
            samlLogoutFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
            metadataDisplayFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
            samlWebSSOProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
            samlWebSSOHoKProcessingFilter()));
    chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
            samlLogoutProcessingFilter()));
    return new FilterChainProxy(chains);
}
 @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .httpBasic()
            .authenticationEntryPoint(samlEntryPoint());
    http
            .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .ignoringAntMatchers("/saml/**");
    http
            .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
            .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class);
    http
            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/saml/logout"))
            .deleteCookies("JSESSIONID")
            .logoutSuccessUrl(config().getSp().getEntityBaseURL() + LOGIN);
    http
            .headers()
            .frameOptions()
            .disable();
    http
            .sessionManagement()
            .sessionAuthenticationErrorUrl(config().getSp().getEntityBaseURL() + LOGIN)
            .invalidSessionUrl(config().getSp().getEntityBaseURL() + LOGIN);
    http
            .sessionManagement()
            .maximumSessions(1)
            .expiredUrl(config().getSp().getEntityBaseURL() + LOGIN);
    http
            .sessionManagement()
            .sessionFixation()
            .newSession();
    http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.NEVER);

    permitEndpoints(http);
}

public class CustomSimpleUrlLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response,
                                Authentication authentication) throws IOException, ServletException {
        if(config().getSp().getEntityBaseURL().equalsIgnoreCase(this.getDefaultTargetUrl())) {
            URLBuilder builder = new URLBuilder(request.getRequestURL().toString());
            builder.setPath("/");
            builder.setFragment("/login");
            builder.setPort(CmsUtil.getWebServerPort());
            this.setDefaultTargetUrl(builder.buildURL());
        }
        super.onLogoutSuccess(request, response, authentication);
    }
}

please help

Upvotes: 2

Views: 8819

Answers (1)

Puneeth Rai
Puneeth Rai

Reputation: 1193

In order to support global logout IDP server should expose logout URL ( easy way to do is if metadata contains SingleLogoutService) More information you can get it from here https://docs.spring.io/spring-security/reference/servlet/saml2/logout.html

For Local logout you can refer spring saml: How is LOGOUT handled? Is it mandatory to have logout endpoint in IDP metadata xml?

For global logout you just have to remove local=true in the query param in the above accepted answer i.e., just send <baseurl>/saml/logout

More information related logout you can refer to https://docs.spring.io/spring-security-saml/docs/1.0.x-SNAPSHOT/reference/htmlsingle/#configuration-logout

Upvotes: 2

Related Questions