-
Notifications
You must be signed in to change notification settings - Fork 6k
CookieServerCsrfTokenRepository does not add cookie #5766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for the feedback. Generally speaking the reactive repositories won't save the token unless something subscribed to the result. This is because if nothing subscribed, there is no way that the token could be known. Has anything subscribed to the CsrfToken? One option is to use something like this:
This also exposes the token to be automatically available for anything using Spring Security's |
@rwinch I can confirm that the I don't mind adding the controller advice but perhaps it would be good to clarify this in the documentation? In general there's no real documentation about how to obtain the CSRF token, which is why I assumed it would be added to any response. Edit: On second thought, isn't the whole idea of the |
Perhaps this does make sense to change the behavior of the cookie based implementation since a user can technically read the cookie directly. The session based implementation makes no sense to write it unless something subscribes because you cannot actually submit the value unless something is trying to use it. I'm going to need to think about this change a bit. |
What could perhaps work if one could configure a CSRF URL which you can GET and which returns an empty response, apart from whatever is needed by CSRF. In this case this would be a cookie, in other TokenRepositories it could be a response header, etc |
I'm not sure I understand the suggestion. User's can already provide a URL that subscribes to the CSRF token which would write it out, so this would be a nothing to change. Something else you could do is to create a ServerCsrfTokenRepository that delegates to another implementation and subscribes on creation of the token. This would ensure that the value is saved eagerly. |
Ah, I was unaware that this URL could already be configured. I didn't see anything in Anyway, for now I will create the customized TokenRepository, that should definitely work. That's a satisfactory solution for now, but I'm still finding myself a bit confused that just enabling the |
I think of the custom repository as more of a work around. In regards to the URL, I'm just speaking of something like the Controller Advice I provided. you could do something similar with a specific URL that resolves the token for you. This is not something Spring Security needs to do for you. |
Sorry, that's what I meant as well: The workaround is fine for now. If in the future the behaviour is changed to make this functionality a bit more intuitive (well...at least to the likes of me :D) then that would certainly be welcomed. |
Ok. Thanks for the response. I'm going to reopen the issue since we agree it would be nice for some sort of support for this. I still don't know exactly how we will go about it yet. |
For a RESTful service I implemented this as a public class ServerCsrfTokenSubscribingResponseModifier extends ResponseBodyResultHandler {
public ServerCsrfTokenSubscribingResponseModifier(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
super(writers, resolver, registry);
setOrder(99);
}
@Override
public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
return Optional.ofNullable(exchange.getAttribute(CsrfToken.class.getName()))
.filter(Mono.class::isInstance)
.map(Mono.class::cast)
.orElseGet(Mono::empty)
.then(super.handleResult(exchange, result));
}
} I agree though it would be much better for this to work out of the box. |
A solution in Kotlin that let the backend of my Webflux application work with Angular frontend, maybe someone find it useful: import com.crl.crlproxy.common.Constants
import mu.KotlinLogging
import org.springframework.http.ResponseCookie
import org.springframework.security.web.server.csrf.CsrfToken
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
import java.time.Duration
private val logger = KotlinLogging.logger {}
@Component
class CsrfHelperFilter : WebFilter {
override fun filter(serverWebExchange: ServerWebExchange,
webFilterChain: WebFilterChain): Mono<Void> {
val key = CsrfToken::class.java.name
val csrfToken: Mono<CsrfToken> = serverWebExchange.getAttribute(key) ?: Mono.empty()
return csrfToken.doOnSuccess { token ->
val cookie = ResponseCookie.from(Constants.CSRF_COOKIE_NAME, token.token)
.maxAge(Duration.ofHours(1))
.httpOnly(false)
.path("/")
.build()
logger.debug { "Cookie: $cookie" }
serverWebExchange.response.cookies.add(Constants.CSRF_COOKIE_NAME, cookie)
}.then(webFilterChain.filter(serverWebExchange))
}
} Additionally you need to register CookieServerCsrfTokenRepository with the corresponding cookie name. Important is CsrfToken class package. |
The non-reactive version of the csrf filter generates the token even if the request doesn't match |
@rwinch I am a new spring user. How would I trigger a subscription when using functional end points. I believe @Controlleradvice will not work with Webflux. Or how can I retrieve the csrf token using router functions? |
this is really very unintuitive. i was also wondering why the cookie is not set. now i ended up here and i still dont see a clear intuitive solution in this conversation. |
I am in the same boat. I have been trying for more than a day now to find a solution that works to includes CSRF information for ajax requests from a non-templated front end, and using functional routing. There doesn't seem to be a good example or pattern, though I'm about to give @plewand 's CsrfHelperFilter a try. |
@plewand 's solution did finally result in my having access to a CSRF token in a cookie for my frontend, though I'm still working out having Spring Security actually accept it on a subsequent POST request. |
The workaround works fine, even if you don't explicitly set the cookie yourself. Just subscribing is enough, i.e. (Kotlin, sorry):
|
Why not implementing the same behavior in Webflux as in Webmvc ? |
I tend to agree that this should remain open as a bug. This step seems like it should be unnecessary; correct configuration of the CookieServerCsrfTokenRepository should result in the cookie being utilized in the same manner as it would for WebMVC. |
Seems odd to require a workaround to add the CsrfToken for CookieServerCsrfTokenRepository. I'm a reactive rookie but this is my what I currently have working for my functional routes (since
@rwinch could we re-open this and think about a more intuitive experience? See upvotes on #5766 (comment) above (currently 10!) Thanks! |
I spent about 3 hours debugging the source code trying to understand why no cookie was set until i stumbled upon this issue. This seems still be the case, there should be some form of documentation about this in the official documentation. This should be fixed in general, as it is counter intuitive. |
Here is the Java Version just for reference: import java.time.Duration;
import org.springframework.http.ResponseCookie;
import org.springframework.security.web.server.csrf.CsrfToken;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class CsrfHelperFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String key = CsrfToken.class.getName();
Mono<CsrfToken> csrfToken = null != exchange.getAttribute(key) ? exchange.getAttribute(key) : Mono.empty();
return csrfToken.doOnSuccess(token -> {
ResponseCookie cookie = ResponseCookie.from("XSRF-TOKEN", token.getToken()).maxAge(Duration.ofHours(1))
.httpOnly(false).path("/").build();
log.debug("Cookie: {}", cookie);
exchange.getResponse().getCookies().add("XSRF-TOKEN", cookie);
}).then(chain.filter(exchange));
}
} |
This is the big WebFlux and MVC difference. |
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed. |
Pardon our dust here as we do some issue cleanup. Feedback was already provided earlier, and I don't think the ticket has been fully addressed, yet, so let's keep the issue open. |
Any update on this? |
There are no updates on this. To answer everyone's question, the reason that it is never persisted if it isn't subscribed to is that we attempt to avoid work that will not ever be valuable. If an application never subscribes to the So my question is this: Is there a functional reason that you want the CsrfToken to be persisted before being subscribed to? My fear is that if we do this eagerly, there are going to be just as many people upset that we are eagerly doing work that does not yet provide value. |
Hi, I've been trying devstartshop's workaround but it doesn't work properly. It creates an XSRF-TOKEN but the value never changes. The value of an XSRF-TOKEN should change for each request (or an option should at least allow that). Every query should provide a new value in response headers and the most recent will be used to call the next endpoint. If an xsrf token is session-scoped I really don't see the added value with a user-session token. |
I'm not sure I understand why the request shouldn't be larger when I configure a cookie repo of anything (or the session bigger when I choose a session repo): when I configure a repo, it is to store something and, to me, it seems accepted that this something is going to use some space where it is stored. In other words, why configuring a cookie repo if nothing is stored in cookies? When can it be of any use? What is the value of requiring an explicit subscription to the CSRF cookie, when I don't have to subscribe to the session cookie for instance? My usage of the the CSRF cookie repository is for JS applications (Angular, React, Vue, ...). How am I supposed to "not eagerly" provide the CSRF token to such applications? |
Uh oh!
There was an error while loading. Please reload this page.
Summary
I have modified the https://github.com/rwinch/spring-security-sample boot-webflux branch to add CSRF using the
CookieServerCsrfTokenRepository
.Actual Behavior
If I do a GET to localhost:8080 I do not see a CSRF cookie being set.
Expected Behavior
A cookie is set so that on subsequent requests I can extract the CSRF token from there and pass it along using a CSRF header.
Configuration
I have modified the boot-webflux WebSecurityConfig like so:
Version
I am using Spring Boot 2.1.0.RC2 which uses Spring Security 5.1.0.RC1.
Sample
See https://github.com/RoyJacobs/spring-security-cookie-repro
The text was updated successfully, but these errors were encountered: