Skip to content
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

CSRF: exception thrown when authentication falied #30594

Closed
FroMage opened this issue Jan 25, 2023 · 21 comments · Fixed by #30672
Closed

CSRF: exception thrown when authentication falied #30594

FroMage opened this issue Jan 25, 2023 · 21 comments · Fixed by #30672

Comments

@FroMage
Copy link
Member

FroMage commented Jan 25, 2023

Describe the bug

With the CSRF extension, I have some tests that require authentication and check that I should get a 401 when not authenticated, that now throw a 500 error:

2023-01-24 17:44:31,988 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-0) Request failed: java.lang.IllegalStateException: CSRF Filter should have set the property csrf_token, but it is null
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter.filter(CsrfRequestResponseReactiveFilter.java:190)
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter$GeneratedServerResponseFilter$filter.filter(Unknown Source)
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter$GeneratedServerResponseFilter$filter_ClientProxy.filter(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.ResourceResponseFilterHandler.handle(ResourceResponseFilterHandler.java:25)
	at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:123)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
	at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:48)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:23)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:10)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:435)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:413)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkiverse.renarde.util.AuthenticationFailedExceptionMapper.myFilter(AuthenticationFailedExceptionMapper.java:95)
	at io.quarkiverse.renarde.util.AuthenticationFailedExceptionMapper_RouteHandler_myFilter_dc247f9502a33787ac476eab0f2e905053c2515c.invoke(Unknown Source)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:97)
	at io.quarkus.vertx.web.runtime.RouteHandler.handle(RouteHandler.java:22)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.security.HttpAuthorizer.doPermissionCheck(HttpAuthorizer.java:118)
	at io.quarkus.vertx.http.runtime.security.HttpAuthorizer.checkPermission(HttpAuthorizer.java:101)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$3.handle(HttpSecurityRecorder.java:207)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$3.handle(HttpSecurityRecorder.java:199)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:376)
	at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:369)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2$3$1.onItem(HttpSecurityRecorder.java:125)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2$3$1.onItem(HttpSecurityRecorder.java:115)
	at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
	at io.smallrye.mutiny.operators.uni.builders.DefaultUniEmitter.complete(DefaultUniEmitter.java:37)
	at io.smallrye.mutiny.groups.UniOnNull.lambda$failWith$1(UniOnNull.java:46)
	at io.smallrye.context.impl.wrappers.SlowContextualBiConsumer.accept(SlowContextualBiConsumer.java:21)
	at io.smallrye.mutiny.groups.UniOnItem.lambda$transformToUni$4(UniOnItem.java:178)
	at io.smallrye.context.impl.wrappers.SlowContextualConsumer.accept(SlowContextualConsumer.java:21)
	at io.smallrye.mutiny.operators.uni.builders.UniCreateWithEmitter.subscribe(UniCreateWithEmitter.java:22)
	at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
	at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:81)
	at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:57)
	at io.smallrye.mutiny.operators.uni.builders.UniCreateFromKnownItem$KnownItemSubscription.forward(UniCreateFromKnownItem.java:38)
	at io.smallrye.mutiny.operators.uni.builders.UniCreateFromKnownItem$KnownItemSubscription.access$100(UniCreateFromKnownItem.java:26)
	at io.smallrye.mutiny.operators.uni.builders.UniCreateFromKnownItem.subscribe(UniCreateFromKnownItem.java:23)
	at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
	at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni.subscribe(UniOnItemTransformToUni.java:25)
	at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
	at io.smallrye.mutiny.groups.UniSubscribe.withSubscriber(UniSubscribe.java:52)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2$3.onItem(HttpSecurityRecorder.java:115)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2$3.onItem(HttpSecurityRecorder.java:101)
	at io.smallrye.mutiny.operators.uni.UniMemoizeOp.drain(UniMemoizeOp.java:160)
	at io.smallrye.mutiny.operators.uni.UniMemoizeOp.onItem(UniMemoizeOp.java:180)
	at io.smallrye.mutiny.operators.uni.builders.UniCreateFromItemSupplier.subscribe(UniCreateFromItemSupplier.java:29)
	at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
	at io.smallrye.mutiny.operators.uni.UniMemoizeOp.subscribe(UniMemoizeOp.java:84)
	at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
	at io.smallrye.mutiny.groups.UniSubscribe.withSubscriber(UniSubscribe.java:52)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2.handle(HttpSecurityRecorder.java:101)
	at io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder$2.handle(HttpSecurityRecorder.java:60)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$15.handle(VertxHttpRecorder.java:618)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$15.handle(VertxHttpRecorder.java:601)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:185)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:160)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
	at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:55)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:158)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:145)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:157)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:99)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:120)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)

Looking at the stack trace, this looks like we got an authentication failure, went to an exception mapper (io.quarkiverse.renarde.util.AuthenticationFailedExceptionMapper.myFilter) which led us to the CSRF response filter, without perhaps going through the request filter first?

Not sure if this is a bug in the CSRF response filter, or in RESTEasy Reactive where we go through response filters without going through request filters first.

Expected behavior

No response

Actual behavior

No response

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

No response

GraalVM version (if different from Java)

No response

Quarkus version or git rev

No response

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

No response

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

I'm getting the same 500 for all 404 endpoints as well. This is because we throw the NotFoundException from the ClassRoutingHandler before we invoke any request handlers. Then we go to the abort chain, which maps the exception and invokes the response filters. Without going through the request filters. I wonder what the spec says about this.

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

At least in the case of the 404, I think it's correct to not invoke the (post-matching) request filters because no endpoint is matched. As for authentication, not sure.

But in any case, the CSRF filter doesn't appear to work in conditions outside of lab testing :(

@sberyozkin
Copy link
Member

sberyozkin commented Jan 25, 2023

Hi Steph @FroMage
Interesting.

So what happens is, the authentication failure is thrown before the JAX-RS chain is even invoked, therefore the CSRF request filter was skipped, but because it was mapped by the JAX-RS exception mapper, the response is routed through the JAX-RS response chain (which I think is correct from the JAX-RS point of view) and the CSRF response filter is seeing the illegal state since the CSRF request filter was not even invoked.

I think it shows that mapping the proactive-authentication exceptions is risky and I don't think it is a CSRF filters problem as such.
You should have 2 workarounds, 1) use the Vert.x filter to map the authenticaton exception 2) disable the proactive authentication (not sure it will work but worth a try).

IMHO the proper solution is to make sure that the mapped authentication failure responses are not routed through the JAX-RS response chain.
CC @michalvavrik @geoand @stuartwdouglas

@sberyozkin
Copy link
Member

sberyozkin commented Jan 25, 2023

If I understand it correctly,
https://github.com/quarkusio/quarkus/blob/main/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/RestInitialHandler.java#L51 should only map the exception but do not .run() if it is authentication exception, perhaps it is simpler to have a method

class RestInitialHandler {
public void handleException(Object externalHttpContext, Throwable throwable) {
        ResteasyReactiveRequestContext rq = requestContextFactory.createContext(deployment, externalHttpContext,
                requestContext,
                initialChain, deployment.getAbortHandlerChain());
        rq.handleException(throwable);
        // and add something to write that mapped response
    }
}

and change all occurrences of RestInitialHandler#beginProcessing with RestInitialHandler#handleException in the
https://github.com/quarkusio/quarkus/blob/main/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java#L219..L268 block

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

Well, all sorts of exceptions can lead to the response chain being invoked while the request chain has been aborted. I gave you 404 as an example, but all sorts of other reasons can cause this, including mismatching content types, other request filters aborting or throwing…

So, IMO it's your response filter that should handle creating the CSRF cookie bytes if the request chain wasn't called. Simple ;)

@sberyozkin
Copy link
Member

sberyozkin commented Jan 25, 2023

@FroMage But the request filter creates the actual token value which will need to be injected in the response HTML form to match this cookie.
Perhaps the response filter can just do nothing if it does not detect the token value

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

The request filter is useful. Keep it. But it doesn't make sense for the response filter to throw if the request filter hasn't been invoked. If the request filter hasn't been invoked, then the user's endpoint also hasn't been invoked, so nothing will use that CSRF value you generated. So you can safely generate it in the response filter, and set the cookie and move on to another bug ;)
Trivial solution.

@sberyozkin
Copy link
Member

sberyozkin commented Jan 25, 2023

So you can safely generate it in the response filter, and set the cookie and move on to another bug ;)

Not really, no point in generating this cookie if someone was doing GET expecting a form and this form would not be coming due to 401. This cookie should only be created if the response filter knows the token was created by the request filter, otherwise it would be just us adding unnecessary cookies to the client. If we create a new cookie and it was for example a Multipart Form request with the embedded token which failed due to Basic Auth failure, then when the user retries and authenticates correctly, the CSRF request filter will fail the request because now the cookie is new but the form token is old

Just doing nothing is safe and simple IMHO - if it was POST form, the same matching cookie and form token would be returned once the authentication succeeds, if it is GET, then no new cookie is required either .

Does it sound good to you ? I'll open a PR if we agree

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

Are these tokens invalidated by submitting a form?

@sberyozkin
Copy link
Member

If the verification is required then the token will be expected and matched against the cookie, if it is GET then it will be created for the response form to have it injected.
If we want to create a new token even for POST forms then it would only make sense if the POST response returns a new form, so I'd consider doing it if it will be necessary/required by the config.
As far as this issue is concerned I propose just to skip creating the cookie in the response filter if the request filter did not create a token

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

What I mean is: when are these tokens invalidated? After each form submission? If not, and they're per-session, and set by a cookie, then I don't see the point of avoiding to set it even from responses that didn't go via the request filter chain.

The cookie will be set, and sent back on any subsequent GET/POST request and you won't have to recreate it.

@sberyozkin
Copy link
Member

@FroMage the filter does not invalidate the tokens. It requires them be present in the form payload if the verification is required. If the verification is required:

  • both cookie and the token are present, match - good, do not match- 401
  • only cookie is present - 401
  • only token is present - 401

I don't see the point of avoiding to set it even from responses that didn't go via the request filter chain.

So how this cookie will be used ?

@FroMage
Copy link
Member Author

FroMage commented Jan 25, 2023

Well it will be used by whatever client code needs to extract the token, and it won't have to be re-created at the next request.

@sberyozkin
Copy link
Member

sberyozkin commented Jan 25, 2023

@FroMage The client is the browser showing the form to the user. OK, let me try various scenarios:

  1. Authenticated GET is OK, CSRF request filter creates the token, response filter the cookie, the follow up POST form will be successfully verified to have matching cookie and form token values.
  2. Authenticated GET fails, CSRF request filter does not create a token, response filter creates the cookie. What is the point ? The user will have to re-authenticate, when sequence 1. above will run. To have some kind of optimization for the follow up authenticated GET not to have the cookie created ? But it won't work because as part of 1. the token will be created and if we skip creating the cookie because it happens to exist then the verification will fail in the follow up POST due to the mismatch of cookie and token values.

I'm not sure what we can't agree upon, is the request filter did not run, just don't try to compensate by running the response filter in any case, this is the right approach IMHO

@sberyozkin
Copy link
Member

Steph, this should be as simple as https://github.com/sberyozkin/quarkus/tree/csrf_authentication_failure, I'm yet to add a test confirming the exception mapper was used in case of the failed authentication

@FroMage
Copy link
Member Author

FroMage commented Jan 26, 2023

You seem to be under the impression that a POST will follow a GET immediately, but it won't. You can have applications with mostly GET requests, and some POST some times. The first GET will grant you a cookie and it will remain for the duration of the exchanges.

So sure, you can delay the first cookie to the first successful GET, as you propose. But I don't see the point. The idea is to get that cookie to the user, and then forget about it until we need it for a POST. I mean, what you propose isn't wrong or worse than what I'm proposing. But mine seems easier and at least we guarantee that the cookie comes from the first GET request, successful or not. It's simpler to explain and describe and rely on.

One thing for sure, the response filter should not throw exceptions and alter the original response.

@sberyozkin
Copy link
Member

sberyozkin commented Jan 27, 2023

@FroMage But Steph, did you check the sequence 1 and 2 above. Let me reiterate what will happen if we create a cookie without the request filter running:

  • GET with bad authentication fails, no request filter runs, response filter creates a cookie
  • If another good GET follows, the request filter will create a token, the response filter must create a matching cookie, the one we created when GET failed is redundant since its value would not match the token value so we can't reuse that cookie.

So sure, you can delay the first cookie to the first successful GET, as you propose.

Looks like we have some misunderstanding here, my PR is only about not creating a cookie for the first failed GET, because in this case this cookie can not be used anywhere, I honestly can't see how. If you can describe exactly how a cookie created with the first failed GET can be used then I'll be happy to change the PR

One thing for sure, the response filter should not throw exceptions and alter the original response.

Sounds good :-)

@sberyozkin
Copy link
Member

@FroMage You are right it won't harm if the cookie is created with the first failed GET, but if we don't see the actual use of such a cookie then it is a cost, even if minor, creating it for every failed GET in Quarkus, that can be avoided, the PR is ready now

@FroMage
Copy link
Member Author

FroMage commented Jan 30, 2023

There's no cost, it's going to be saved by the client, and sent back for the next request and we won't re-create it for future calls.

@FroMage
Copy link
Member Author

FroMage commented Jan 30, 2023

Of course cookies sent via a non-200 request are still used by the browser. Why would you think not?

@sberyozkin
Copy link
Member

@FroMage Looks like we are going in circles,

Can you please type a sequence here, 1., 2., 3. etc, proving creating a cookie in the CSRF response filter without the CSRF request filter running will be useful to the CSRF feature ?

benkard added a commit to benkard/mulkcms2 that referenced this issue Apr 2, 2023
This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [flow-bin](https://github.com/flowtype/flow-bin) ([changelog](https://github.com/facebook/flow/blob/master/Changelog.md)) | devDependencies | minor | [`^0.199.0` -> `^0.200.0`](https://renovatebot.com/diffs/npm/flow-bin/0.199.0/0.200.0) |
| [com.rometools:rome](http://rometools.com) ([source](https://github.com/rometools/rome)) | compile | minor | `1.18.0` -> `1.19.0` |
| [org.postgresql:postgresql](https://jdbc.postgresql.org) ([source](https://github.com/pgjdbc/pgjdbc)) | build | patch | `42.5.3` -> `42.5.4` |
| [org.jsoup:jsoup](https://jsoup.org/) ([source](https://github.com/jhy/jsoup)) | compile | patch | `1.15.3` -> `1.15.4` |
| [io.quarkus:quarkus-maven-plugin](https://github.com/quarkusio/quarkus) | build | patch | `2.16.2.Final` -> `2.16.3.Final` |
| [io.quarkus:quarkus-universe-bom](https://github.com/quarkusio/quarkus-platform) | import | patch | `2.16.2.Final` -> `2.16.3.Final` |

---

### Release Notes

<details>
<summary>flowtype/flow-bin</summary>

### [`v0.200.0`](flow/flow-bin@9618443...b6c1eb0)

[Compare Source](flow/flow-bin@9618443...b6c1eb0)

### [`v0.199.1`](flow/flow-bin@05bb4e3...9618443)

[Compare Source](flow/flow-bin@05bb4e3...9618443)

</details>

<details>
<summary>rometools/rome</summary>

### [`v1.19.0`](https://github.com/rometools/rome/releases/tag/1.19.0)

[Compare Source](rometools/rome@1.18.0...1.19.0)

<!-- Release notes generated using configuration in .github/release.yml at 1.19.0 -->

#### What's Changed

##### 🔨 Dependency Upgrades

-   Bump flatten-maven-plugin from 1.2.7 to 1.3.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#565
-   Bump maven-bundle-plugin from 5.1.5 to 5.1.8 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#563
-   Bump maven-dependency-plugin from 3.3.0 to 3.5.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#602
-   Bump maven-deploy-plugin from 2.8.2 to 3.1.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#607
-   Bump maven-jar-plugin from 3.2.2 to 3.3.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#574
-   Bump maven-javadoc-plugin from 3.3.1 to 3.5.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#609
-   Bump maven-scm-plugin from 1.12.2 to 1.13.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#554
-   Bump assertj-core from 3.22.0 to 3.24.2 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#603
-   Bump slf4j-api from 1.7.36 to 2.0.6 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#596

##### Other Changes

-   Bump actions/setup-java from 3.3.0 to 3.10.0 by [@&#8203;dependabot](https://github.com/dependabot) in rometools/rome#606
-   Bump logback-classic from 1.2.10 to 1.3.5 by [@&#8203;PatrickGotthard](https://github.com/PatrickGotthard) in rometools/rome#611

**Full Changelog**: rometools/rome@1.18.0...1.19.0

</details>

<details>
<summary>pgjdbc/pgjdbc</summary>

### [`v42.5.4`](https://github.com/pgjdbc/pgjdbc/blob/HEAD/CHANGELOG.md#&#8203;4254-2023-02-15-102104--0500)

##### Fixed

fix: fix testGetSQLTypeQueryCache by searching for xid type. We used to search for box type but it is now cached. xid is not cached, this nuance is required for the test.
fix OidValueCorrectnessTest BOX_ARRAY OID, by adding BOX_ARRAY to the oidTypeName map \[MR [#&#8203;2810](pgjdbc/pgjdbc#2810.
fixes [Issue #&#8203;2804](pgjdbc/pgjdbc#2804).
fix: Make sure that github CI runs tests on all [MRs #&#8203;2809](\(pgjdbc/pgjdbc#2809)).

</details>

<details>
<summary>quarkusio/quarkus</summary>

### [`v2.16.3.Final`](https://github.com/quarkusio/quarkus/releases/tag/2.16.3.Final)

[Compare Source](quarkusio/quarkus@2.16.2.Final...2.16.3.Final)

##### Major changes

-   [#&#8203;29756](quarkusio/quarkus#29756) - Support custom Flyway credentials/URL

##### Complete changelog

-   [#&#8203;31141](quarkusio/quarkus#31141) - Resolve roles allowed configuration expression after config setup is completed
-   [#&#8203;31129](quarkusio/quarkus#31129) - Fix stuck HTTP2 request when sent challenge has resumed request
-   [#&#8203;31125](quarkusio/quarkus#31125) - Add "keep-alive-enabled" parameter to REST client reactive
-   [#&#8203;31112](quarkusio/quarkus#31112) - Qute - fix assignability check when validating expressions
-   [#&#8203;31099](quarkusio/quarkus#31099) - Use the effective Maven project build config when initializing sources and classes paths for dev mode
-   [#&#8203;31092](quarkusio/quarkus#31092) - Make sure quarkus:go-offline properly supports test scoped dependencies
-   [#&#8203;31077](quarkusio/quarkus#31077) - Qute: regression in template extension method with byte array
-   [#&#8203;31076](quarkusio/quarkus#31076) - Quarkiverse: Install instead of verify
-   [#&#8203;31074](quarkusio/quarkus#31074) - Added quarkus-jms-spi to quarkus bom
-   [#&#8203;31059](quarkusio/quarkus#31059) - Path lookup must also consider interfaces
-   [#&#8203;31046](quarkusio/quarkus#31046) - Simplify Quarkiverse release.yml workflow
-   [#&#8203;31038](quarkusio/quarkus#31038) - Update Instrumentation Processor check logic to match comment
-   [#&#8203;31036](quarkusio/quarkus#31036) - Use CDI when accessing UserTransaction/TransactionManager in QuarkusTransaction
-   [#&#8203;31028](quarkusio/quarkus#31028) - Fix typo in snapstart enable config
-   [#&#8203;31016](quarkusio/quarkus#31016) - Re-initialize platform dependent netty classes/values at runtime
-   [#&#8203;30988](quarkusio/quarkus#30988) - Race condition in SmallRye Config property expansion for [@&#8203;RolesAllowed](https://github.com/RolesAllowed) value?
-   [#&#8203;30964](quarkusio/quarkus#30964) - Add ConfigMappings from a builder class to support full hot reload
-   [#&#8203;30961](quarkusio/quarkus#30961) - Error of quarkus:dev when the project.build.directory is overridden by a profile
-   [#&#8203;30960](quarkusio/quarkus#30960) - Register CDI Bean when ConfigMapping is marked as Unremovable
-   [#&#8203;30922](quarkusio/quarkus#30922) - Fix dependency parsing in JBangBuilderImpl
-   [#&#8203;30885](quarkusio/quarkus#30885) - Add concurrency configuration to the GitHub Action workflows
-   [#&#8203;30843](quarkusio/quarkus#30843) - Micrometer-Extension writes wrong URI-Tag when Path-Variables defined at Interface-Level
-   [#&#8203;30672](quarkusio/quarkus#30672) - Avoid creating CSRF cookie if no CSRF token was created
-   [#&#8203;30648](quarkusio/quarkus#30648) - Support passing filename to multipart form data output
-   [#&#8203;30594](quarkusio/quarkus#30594) - CSRF: exception thrown when authentication falied
-   [#&#8203;30570](quarkusio/quarkus#30570) - Set filename for PartItems in MultipartFormDataOutput
-   [#&#8203;30455](quarkusio/quarkus#30455) - Introduce `quarkus.datasource.devservices.init-script-path`
-   [#&#8203;29756](quarkusio/quarkus#29756) - Support custom Flyway credentials/URL
-   [#&#8203;29631](quarkusio/quarkus#29631) - [@&#8203;Unremovable](https://github.com/Unremovable) ConfigMapping is still removed
-   [#&#8203;29630](quarkusio/quarkus#29630) - Changes to configmappings not being applied during hot reload
-   [#&#8203;28709](quarkusio/quarkus#28709) - QuarkusTransaction does not fire [@&#8203;Initialized](https://github.com/Initialized)(TransactionScoped.class)
-   [#&#8203;24639](quarkusio/quarkus#24639) - configure dedicated db user for database migrations: DML-only user for datasource, but DDL user for migration
-   [#&#8203;23360](quarkusio/quarkus#23360) - "Request has already been read" using vertx + auth
-   [#&#8203;17839](quarkusio/quarkus#17839) - Invalid memory configuration for netty maxDirectMemory in native image

</details>

<details>
<summary>quarkusio/quarkus-platform</summary>

### [`v2.16.3.Final`](quarkusio/quarkus-platform@2.16.2.Final...2.16.3.Final)

[Compare Source](quarkusio/quarkus-platform@2.16.2.Final...2.16.3.Final)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

👻 **Immortal**: This MR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4yNC4wIiwidXBkYXRlZEluVmVyIjoiMzQuMjQuMCJ9-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants