I am using Spring Security with SAML2 service provider and have a requirement to set custom tokens as part of cookies in the response after a successful SAML2 login.I have created a filter SetToken and configure to call after Saml2WebSsoAuthenticationFilter. The filter is getting called every time, whereas our requirement is to set custom cookie once after the SAML2 login is successful.

Here is my Spring configuration class:

@Configuration
public class SAMLConfiguration {

    @Autowired
    SetToken token;

    @Bean
    SecurityFilterChain configure(HttpSecurity http) throws Exception {
        OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();

        Saml2MetadataFilter filter = new Saml2MetadataFilter(relyingPartyRegistrationRepository, new OpenSamlMetadataResolver());
        http.addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class);

        http.authorizeHttpRequests(requests -> requests
                .requestMatchers("/saml2/service-provider-metadata/**")
                .permitAll()
        ).addFilterAfter(token, Saml2WebSsoAuthenticationFilter.class)
        .addFilterAfter(refresh, SecurityContextHolderFilter.class)
        .saml2Login(saml2 -> saml2
                .authenticationManager(new ProviderManager(authenticationProvider))
                .relyingPartyRegistrationRepository(relyingPartyRegistrationRepository)
        ).saml2Logout(withDefaults());
    }
}

The SetToken filter, which is supposed to be called after SAML2 authorization, is defined as follows, this class is getting called every time which we do not want as token setting happens only once.

@Component
public class SetToken extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // Get principle and make an API call to a third-party system to set the cookie
        // SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        response.addCookie(customCookie);
        filterChain.doFilter(request, response);
    }
}

I also tried setting the session authentication strategy in the configuration, but it is also getting invoked in every request after a successful SAML2 login. We want to attach a handler that is only invoked once after a successful SAML2 login.

http.sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .sessionAuthenticationStrategy(tokenStrategy);

The TokenStrategy class implements SessionAuthenticationStrategy and is defined as follows:

public class TokenStrategy implements SessionAuthenticationStrategy {

    @Override
    public void onAuthentication(
            Authentication authentication,
            HttpServletRequest request,
            HttpServletResponse response
    ) throws SessionAuthenticationException {
        // Custom logic to get the cookie
        response.addCookie(customCookie);
    }
}

How can I attach a one-time event listener that is executed only after a successful SAML2 login? Is there a way to have a filter that is only called after the SAML2 filter successfully logs in and is not invoked in any other api/url calls?

Comment From: marcusdacoregio

Hi @sumeetpri, have you tried adding your logic to an AuthenticationSuccessHandler and using that handler in the SAML configuration? It would look like this:

@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
    // ...
    http.saml2Login(saml2 -> saml2
        .successHandler(new MySetCookieSuccessHandler())
    );
    return http.build();
}

This seems more like a question than a bug in Spring Security therefore I'm tagging this as for StackOverflow. If the setup above does not work properly we can reopen this to check the issue.

Comment From: sumeetpri

Thanks it worked.

Comment From: martinwunderlich-celonis

I realize this ticket has been closed, but I just want to add a comment to it for anyone else stumbling over this: When using your own custom handler for SAML authentication success, make sure to not set the defaultSuccessUrl(...) in the configurer at the same time. If that Url is set, the custom success handler will be ignored and replaced with Spring's default SavedRequestAwareAuthenticationSuccessHandler. Quite a gotcha...

In other words, this here won't work as expected:

@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
    // ...
    http.saml2Login(saml2 -> saml2
        .successHandler(new MySetCookieSuccessHandler())
        .defaultSuccessUrl("/")
    );
    return http.build();
}