Fork me on GitHub
#ring
<
2024-06-15
>
Eugen02:06:23

hi, How can I invalidate a ring session ? I'm working to integrate pac4j with ring and I've gotten quite far. One situation that I don't know to handle is session invalidate to implement renew-session. Basically copy the session to a new session ID. There is no support for this OOTB with ring-session middleware. I also miss a function to get the current session id from a request. I guess a var could be used to store the key name for the session. I could probably implement this once I figure things out, but it's a recurring pattern that IMO should exist as part of the library / docs. If I'm not the only one that thinks like this I will open an issue and maybe we can get the functionality implemented.

hiredman02:06:10

Dunno if it is documented, but if add a :recreate key to the session middleware with a truthy value it will delete and create a new session

Eugen03:06:45

thank you

weavejester10:06:17

It's documented in the wiki (https://github.com/ring-clojure/ring/wiki/Sessions) but should also probably be in the docstring of wrap-session.

Eugen11:06:56

thank you. If I ever get some free time I would like to improve the docs. Clojure tools / libs generally lack begginner friendly documentation IMO 😞

Eugen11:06:03

during my work integrating pac4j (ongoing) I also think an alternative session implementation where the store is passes out of band and not via request / response maps might be ok as well. pac4j expects a mutable session store (because of Servlet API) and I have to work around that .

Eugen11:06:49

having just the session ID on req / response and passing the store out of band to the handlers might be a solution

weavejester11:06:40

Some sessions are updated via the HTTP response (e.g. serialized session data in encrypted cookies), rather than via some side-channel (such as a database). Ring targets the lowest common denominator.

weavejester11:06:22

However, you can always add middleware to supply a mutable session, e.g.

(defn wrap-mutable-session [handler]
  (fn [request]
    (let [session  (atom (:session request))
          request  (assoc request :mutable-session session)
          response (handler request)]
      (assoc response :session @session))))

Eugen11:06:15

yes, that is true, but the issue I find is that the ecosystem around this is missing in Clojure /ring . take for example ring-session-timeout - it expects the session to be under :session key so it will update that and you get into a divergent sitution https://github.com/ring-clojure/ring-session-timeout/blob/2cf787cb05caebe66c3af5201aaf0c517601f03e/src/ring/middleware/session_timeout.clj#L40 . IMO it would be nice if the session protocol was more extensive and hid the session details from the user. e.g. get-session-id [req] for example could hide the session key from user / implementors. get-session [req] could do the same ?! I can only come up with mutable API's - sorry . My point is, having session under :session - is an implementation detail that should be hidden from users. This can free middleware implementors to store session how they want.

weavejester11:06:33

The wrap-mutable-session example above will work with ring-session-timeout, as long as ring-session-timeout wraps it.

weavejester11:06:59

Can you tell me a little more about what you're trying to do, and why you need a mutable session store?

Eugen11:06:41

sure. I can tell you why I THINK I need it - I don't know if I really do 🙂

Eugen11:06:06

I have this WIP ring pac4j implementation that I am working on https://github.com/netdava/snm/blob/main/dev/ring_pac4j.clj .

Eugen11:06:28

pac4j seesm to have a lot of available Auth Clients

Eugen11:06:47

I have auth working - code is rough, mind your step

Eugen11:06:00

I don't have role assignement to user - so you can't access the /protected link - but you do get rejected proeprly

weavejester11:06:24

If I'm understanding correctly, pac4j is some sort of security framework, and to interface with it you need Java objects for sessions, cookies, etc.

Eugen11:06:09

yes, in a nutshell

Eugen11:06:00

in this case, having both :session and the external session store is more work than having just the external session store

Eugen11:06:31

the :session approach has merit with regards to external (redis) session stores - in the sense that local session is local until the response is sent and then it gets written to the external store

weavejester11:06:48

Is the pac4j session store required only for the lifespan of the handler? Or to put it another way, can it be updated after the response is returned?

Eugen11:06:01

I will have to think about that. I think so. Right now I keep a request + response as atoms and update those and then merge back in the response once it's done. But I just started with this so not sure if it will work with all cases. https://github.com/netdava/snm/blob/a6d257697516336a88cedb38ccd7f05abe813da5/dev/ring_pac4j.clj#L245

weavejester12:06:31

I don't know anything about pac4j, but can't you do something like:

(defn wrap-pac4j [handler options]
  (fn [request]
    (let [config  (make-pac4j-config options)]
          context (make-pac4j-context request)]
      (do-side-effectful-pac4j-stuff config context)
      (let [response (handler request)]
        (merge-pac4j-response response config context)))))
And have the various pac4j objects, such as the context and session store, have local mutable state that can be read by merge-pac4j-response . The idea is that you'd get the relevant information from the request map, convert it into a form pac4j can read, and set up mutable stores to hold what pac4j wants to write. Then when pac4j is done, you can read that mutable data, and convert it back into something you can place on the response map.

Eugen12:06:52

I'll see what I can do and hopefully I will get it working and ask for some feedback to publish it