For a SPA, the current recommendation for configuration CSRF is three-fold:

  1. set the CsrfTokenRepository to CsrfTokenRepository#withHttpOnlyFalse
  2. set the CsrfAttributeHandler to a custom class listed in the reference manual
  3. add a custom filter that "subscribes" to the deferred cookie so that the cookie header is written

The current state of the recommendation could be improved in a way that is less error-prone and requires less custom boilerplate for users.

One possibility is to provide a customizer like so:

.csrf(CsrfCustomizers.spaDefaults())

Where said customizer would apply these three rules for them. I imagine this might look something like the following:

public static Customizer<CsrfConfigurer> spaDefaults() {
    return (csrf) -> csrf
        .csrfTokenRepositorySubscription(CookieCsrfTokenRepository.withHttpOnlyFalse())
        .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())
    )
}

Where csrfTokenRepositorySubscription is pseudocode for a way to supply the repository and indicate that the filter chain should automatically subscribe to the cookie as part of formulating the response (#3 in the above list) and SpaCsrfTokenRequestHandler is pseudocode for an implementation that is similar to the sample in the reference guide.

Comment From: jzheaux

Note that this can be achieved with the existing DSL in the following way:

```java public static final class CsrfCustomizers { public static Customizer spaDefaults() { return (csrf) -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .csrfTokenHandler(new SpaCsrfTokenRequestHandler()); }

private static final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
    this.xor.handle(request, response, csrfToken);
    csrfToken.get(); // subscribe
}

@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
    String header = request.getHeader(csrfToken.getHeaderName());
    return ((header != null) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
}
}

}

Comment From: AugustoRavazoli

Will SecurityMockMvcRequestPostProcessors.csrf().asHeader() support this?

Comment From: ch4mpy

Would you kindly expose this SpaCsrfTokenRequestHandler publicly? And maybe do the same for the reactive stack?

Most of my OAuth2 clients (with oauth2Login and consumed by single-page or mobile apps) are reactive because this clients are BFFs implemented with spring-cloud-gateway and the TokenRelay filter. So it would be great to benefit the same configuration simplification on the reactive stack too.

Comment From: jzheaux

Sure, @ch4mpy. I think that's something we could consider in the context of this ticket. I think one of the main sticky points is whether the implementation should subscribe in such a way as to ensure that the cookie is written without requiring an extra filter. It's a bit easier to hide that choice behind the context of something more opinionated like the DSL.

Comment From: ch4mpy

whether the implementation should subscribe in such a way as to ensure that the cookie is written

@jzheaux, in my opinion, yes: when I use a customizer or configuration option called *Cookie*Repository, I expect that the cookie is computed and added to the request.

With Boot, it's easy to add csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()).csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) in configuration if a property has a given value and to register a filter @Bean with a @ConditionalOnProperty checking for the same property value (this is something I do already).

I have not tried to find a way to do the same with just a customizer, but that would have the advantage to be usable even without Boot (and activating such a customizer based on properties in a Boot starter would be trivial).

Comment From: kguelzau

Just want to drop a message that this is still needed.

Spent a lot of time in docs and issues figuring out what is the current correct way of to prevent CSRF for SPA in Spring Cloud Gateway (Reactive) with Spring Security. And I am still unsure... * if the issued XSRF-TOKEN Cookie shouldn't be better SameSite = Strict by default * if the Cookie should change it's value on each response