blubb
blubb

Reputation: 9890

Weird session management bug when mixing ring's `wrap-defaults`' and lib-noir's `wrap-noir-session`

I have a ring webapp that uses noir.session as follows:

(def app (-> app-routes
         (session/wrap-noir-session)
         (wrap-defaults site-defaults))) ; both from ring.middleware.defaults

However, it seems like session variables are lost between the requests. The server keeps sending a Set-Cookie header even though the client provides the Cookie header.

Using trial and error, I found out that when I disable ring's anti-forgery wrapper as follows, the same session lives across requests:

(def app (-> app-routes
         (session/wrap-noir-session)
         (wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))

but of course I don't want that. Why is this, and how can I fix my problem without risking CSRF attacks?

Upvotes: 0

Views: 123

Answers (1)

blubb
blubb

Reputation: 9890

Browsing the source code of all involved middleware, I found out that lib-noir's wrap-noir-session re-implements parts of ring's wrap-session. That lead me to the following experiment:

(def app (-> app-routes
         (session/wrap-noir-session {:store (memory-store)})
         (wrap-defaults (assoc site-defaults :session false))))

Here as well, sessions live across requests.

Here's the culprit: wrap-defaults already applies wrap-session, so when the wrap-noir-session handler is listed as well, wrap-session is actually invoked twice.

The final solution couldn't be simpler: Use wrap-noir-session* instead. According to the docs, it "expects that wrap-session has already been used." It seems that the contrary is true for wrap-noir-session.

(def app (-> app-routes
         (session/wrap-noir-session*)
         (wrap-defaults site-defaults)))

Hopefully, this will save you some time.

Upvotes: 2

Related Questions