Search code examples
f#suave

Removing key from session state


(Noting that my total experience with Suave and back-end web-development in general can be measured in days and counted on one hand)

As part of my project, whenever a user successfully signs in a unique GUID is created and stored within the session state (using a SessionID key). Subsequent requests from that same user pass in that same GUID (from the session state) where it is looked up from a map held in server memory in order to confirm authority.

This part works perfectly using the approach from the Suave documentation. To clarify, my routing path uses statefulForSession along with setSessionValue and getSessionValue as defined in the documentation to set and read the SessionID key referred to above.

When a user logs-out or there has not been any activity for a certain period of time, the GUID will be removed from the server held map above. Is it possible to remove a key (SessionID) from the session state? ie... Is there a possible definition for a removeSessionValue?

Given the underlying StateStore object only exposes set and get, it makes me wonder whether what I'm asking is (somewhat) non-sensical or whether there is some other mechanism I should be using.

From the Suave source code, I'm aware that the session state (which is held in a string -> object map) is ultimately serialised and encrypted within a cookie called st.


Update 2021-11-08:

A pull request has since been submitted where an unset method is exposed by the StateStore object.


Solution

  • What you're asking for seems reasonable, but I don't think there's a clean way to do it given the current StateStore API. That said, you can hack the low-level cookie state as follows:

    let removeSessionValue (key : string) : WebPart =
        context (fun ctx ->
            let cookieState =
                {
                    serverKey      = ctx.runtime.serverKey
                    cookieName     = StateCookie
                    userStateKey   = StateStoreType
                    relativeExpiry = Session
                    secure         = false
                }
            updateCookies cookieState (function
                | None ->
                    Map.empty
                        |> ctx.runtime.cookieSerialiser.serialise
    
                | Some data ->
                    try
                        let m = ctx.runtime.cookieSerialiser.deserialise data
                        m
                        |> Map.remove key   // this is where we remove the key
                        |> ctx.runtime.cookieSerialiser.serialise
                    with ex ->
                        Map.empty
                        |> ctx.runtime.cookieSerialiser.serialise))
    

    I copied most of this from the Suave source code and tried it quickly to make sure it works, but I can't guarantee its correctness.