For a SPA, the current recommendation for configuration CSRF is three-fold:
- set the
CsrfTokenRepositorytoCsrfTokenRepository#withHttpOnlyFalse - set the
CsrfAttributeHandlerto a custom class listed in the reference manual - 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
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