Summary

In a web application with Spring Security 4.2.x, RequestCacheConfigurer configures a default request matcher that doesn't allow saving POST requests when csrf is enabled.

Actual Behavior

When we submit a POST request against a secured endpoint the application redirects to the login page. After successful login the original POST request isn't restored so the invocation fails.

Expected Behavior

The original POST request is restored.

Configuration

I'm relying on Spring Security default configurations for the request cache. The key part is here RequestCacheConfigurer :

    private RequestMatcher createDefaultSavedRequestMatcher(H http) {
                ...
                if (isCsrfEnabled) {
            RequestMatcher getRequests = new AntPathRequestMatcher("/**", "GET");
            matchers.add(0, getRequests);
        }

If csrf is enabled only get requests will be cached.

Is there any security related reason why this matcher isn't supporting POST invocations?

Version

4.2.12

Comment From: jzheaux

I believe it simply wouldn't work. Since the CSRF token is bound to the session and the session is invalidated at authentication time, then the token stored as part of the post would fail to match.

Comment From: codependent

Yes, but that POST URL could use a CSRF exclusion. In that case if (isCsrfEnabled) is still true but the POST request can’t be saved.

Comment From: jzheaux

Yes, that's a good point. The RequestCacheConfigurer could possibly consult CsrfConfigurer's RequestMatcher to improve its own decision making. I'll take a look at this.

In the meantime, would this work for you:

@Bean 
RequestCache requestCache() {
    HttpSessionRequestCache cache = new HttpSessionRequestCache();
    cache.setRequestMatcher(myRequestMatcher());
    return cache;
}

where myRequestMatcher() is a request matcher that matches what your csrf() configuration will ignore?

Comment From: codependent

Thank's @jzheaux, I did exactly what you suggest to work around it, but it'd be great if the framework provided that behaviour out of the box ;-)

Comment From: uuufo

Hello! I'm looking to implement a solution like above while retaining the default matcher created by RequestCacheConfigurer in Spring Security 6.1.x:

HttpSessionRequestCache cache = new HttpSessionRequestCache();
cache.setRequestMatcher(new OrRequestMatcher(
        new AntPathRequestMatcher("/post-endpoint", "POST"),
        // matcher created by RequestCacheConfigurer.createDefaultSavedRequestMatcher()
));

Currently I'm replicating the behavior of createDefaultSavedRequestMatcher(), but was wondering if there is a better way?

I've also noticed that this works without excluding the endpoint from CSRF. It seems after login the session ID is updated and the CSRF token is replaced, but then the saved request is first hit using GET (with ?continue) followed by the actual POST request. Does this cause it to be submitted with the new CSRF token? Thanks!