Disable session cookie / alternate session implmentation #100

Open
ajevans85 opened this Issue Nov 24, 2016 · 3 comments

Comments

Projects
None yet
2 participants
@ajevans85

Two questions.

  1. Is it possible to disable Spock from creating the spockcookie session cookie? I can see it's created with Wai middleware but can't an easy way to disable it.

  2. This leads on from I want to use a encrypted cookie to store sessions and not use a remote session store.

I currently create a encrypted session cookie on a callback from Auth0 with a expiry field in the cookie, user id, name and roles. The expiry is low, each request returns a new session cookie with expiry incremented creating a sliding session.

I can see the backend session storage is plugable but no way to disable backend storage and use a client side implementation? Is anything on the roadmap to allow this?

@agrafix

This comment has been minimized.

Show comment
Hide comment
@agrafix

agrafix Nov 25, 2016

Owner

Let's say: It depends :-)

If you use the Spock-core package you have full control over that, the framework will not inject any cookies. But you also don't have CSRF protection built in, no global "state" and no database pooling. The latter two are easy to implement in your own monad stack on top of SpockT.

If you use the Spock package, you currently can not prevent Spock from creating a spockcookie. But I'd be happy to explore how we could integrate a fully client side session management, as I know of several other Spock users that have a similar setup. Are you interested in contributing?

Owner

agrafix commented Nov 25, 2016

Let's say: It depends :-)

If you use the Spock-core package you have full control over that, the framework will not inject any cookies. But you also don't have CSRF protection built in, no global "state" and no database pooling. The latter two are easy to implement in your own monad stack on top of SpockT.

If you use the Spock package, you currently can not prevent Spock from creating a spockcookie. But I'd be happy to explore how we could integrate a fully client side session management, as I know of several other Spock users that have a similar setup. Are you interested in contributing?

@ajevans85

This comment has been minimized.

Show comment
Hide comment
@ajevans85

ajevans85 Nov 25, 2016

Thanks.

I did switch to Spock-core using a ReaderT with my own Connection Pool and cookie Key from the clientsession package. Firefox was still reporting the spockcookie in the inspector after repeatedly deleting it.

Checking again it must of been a Firefox issue, the cookie is no longer appearing. Verifying with curl the Set-Cookie header is not set as expected.

I am new to Haskell / Spock, happy to contribute where I can if you have any implementation suggestions.

Not knowing to much it might be possible using the existing SessionManager type? sm_clearAllSessions and sm_closeSessionManager would be a noop for client side. sm_middleware could have a middleware which automatically increments/validates expiry based on some config value. Not sure about the need for a sessionId clientside but can stay for compatibility. m is the Spock monad so there is access to read / write a cookie?

data SessionManager m conn sess st
   = SessionManager
   { sm_getSessionId :: m SessionId
   , sm_getCsrfToken :: m T.Text
   , sm_regenerateSessionId :: m ()
   , sm_readSession :: m sess
   , sm_writeSession :: sess -> m ()
   , sm_modifySession :: forall a. (sess -> (sess, a)) -> m a
   , sm_mapSessions :: (forall n. Monad n => sess -> n sess) -> m ()
   , sm_clearAllSessions :: MonadIO m => m ()
   , sm_middleware :: Middleware
   , sm_closeSessionManager :: IO ()
   }

ajevans85 commented Nov 25, 2016

Thanks.

I did switch to Spock-core using a ReaderT with my own Connection Pool and cookie Key from the clientsession package. Firefox was still reporting the spockcookie in the inspector after repeatedly deleting it.

Checking again it must of been a Firefox issue, the cookie is no longer appearing. Verifying with curl the Set-Cookie header is not set as expected.

I am new to Haskell / Spock, happy to contribute where I can if you have any implementation suggestions.

Not knowing to much it might be possible using the existing SessionManager type? sm_clearAllSessions and sm_closeSessionManager would be a noop for client side. sm_middleware could have a middleware which automatically increments/validates expiry based on some config value. Not sure about the need for a sessionId clientside but can stay for compatibility. m is the Spock monad so there is access to read / write a cookie?

data SessionManager m conn sess st
   = SessionManager
   { sm_getSessionId :: m SessionId
   , sm_getCsrfToken :: m T.Text
   , sm_regenerateSessionId :: m ()
   , sm_readSession :: m sess
   , sm_writeSession :: sess -> m ()
   , sm_modifySession :: forall a. (sess -> (sess, a)) -> m a
   , sm_mapSessions :: (forall n. Monad n => sess -> n sess) -> m ()
   , sm_clearAllSessions :: MonadIO m => m ()
   , sm_middleware :: Middleware
   , sm_closeSessionManager :: IO ()
   }

@agrafix agrafix added the enhancement label Nov 25, 2016

@agrafix

This comment has been minimized.

Show comment
Hide comment
@agrafix

agrafix Nov 25, 2016

Owner

Actually, the current SessionManager implements a session manager for all m in MonadIO (see

withSessionManager ::
MonadIO m => SessionCfg conn sess st -> SessionIf m -> (SessionManager m conn sess st -> IO a) -> IO a
withSessionManager sessCfg sif =
bracket (createSessionManager sessCfg sif) sm_closeSessionManager
createSessionManager ::
MonadIO m => SessionCfg conn sess st -> SessionIf m -> IO (SessionManager m conn sess st)
createSessionManager cfg sif =
). We should probably split the SessionManager into two (or three) types otherwise the type will not represent the semantics (i.E. sm_mapSessions can not be implemented for client side sessions.) We will also need to track if we use client or server side sessions in the sess parameter somehow we carry around to call/expose the functions to the end user. So to start we'd need to split the type for example like so:

data BaseSessionManager m conn sess st
   = BaseSessionManager
   { bsm_getSessionId :: m SessionId
   , bsm_getCsrfToken :: m T.Text
   , bsm_regenerateSessionId :: m ()
   , bsm_readSession :: m sess
   , bsm_writeSession :: sess -> m ()
   , bsm_modifySession :: forall a. (sess -> (sess, a)) -> m a
   , bsm_middleware :: Middleware
   }
data ServerSessionManager m conn sess st
   = ServerSessionManager
   { ssm_base :: m (BaseSessionManager m conn sess st)
   , ssm_mapSessions :: (forall n. Monad n => sess -> n sess) -> m ()
   , ssm_clearAllSessions :: MonadIO m => m ()
   , ssm_closeSessionManager :: IO ()
   }
data ClientSessionManager m conn sess st
   = ClientSessionManager
   { csm_base :: m (BaseSessionManager m conn sess st)
   , -- what do we need apart from that?
   }

All session values would then be wrapped with either newtype ServerSession sess = ServerSession sess and newtype ClientSession sess = ClientSession sess

The functions in https://github.com/agrafix/Spock/blob/138198fa5e50d7238bfecc64678403aadc80fc37/Spock/src/Web/Spock/SessionActions.hs would then move to type classes IsAnySession, IsServerSession and IsClientSession and be dependent on the session wrapper, as of how the session manager is initialized and chosen.

What do you think?

Owner

agrafix commented Nov 25, 2016

Actually, the current SessionManager implements a session manager for all m in MonadIO (see

withSessionManager ::
MonadIO m => SessionCfg conn sess st -> SessionIf m -> (SessionManager m conn sess st -> IO a) -> IO a
withSessionManager sessCfg sif =
bracket (createSessionManager sessCfg sif) sm_closeSessionManager
createSessionManager ::
MonadIO m => SessionCfg conn sess st -> SessionIf m -> IO (SessionManager m conn sess st)
createSessionManager cfg sif =
). We should probably split the SessionManager into two (or three) types otherwise the type will not represent the semantics (i.E. sm_mapSessions can not be implemented for client side sessions.) We will also need to track if we use client or server side sessions in the sess parameter somehow we carry around to call/expose the functions to the end user. So to start we'd need to split the type for example like so:

data BaseSessionManager m conn sess st
   = BaseSessionManager
   { bsm_getSessionId :: m SessionId
   , bsm_getCsrfToken :: m T.Text
   , bsm_regenerateSessionId :: m ()
   , bsm_readSession :: m sess
   , bsm_writeSession :: sess -> m ()
   , bsm_modifySession :: forall a. (sess -> (sess, a)) -> m a
   , bsm_middleware :: Middleware
   }
data ServerSessionManager m conn sess st
   = ServerSessionManager
   { ssm_base :: m (BaseSessionManager m conn sess st)
   , ssm_mapSessions :: (forall n. Monad n => sess -> n sess) -> m ()
   , ssm_clearAllSessions :: MonadIO m => m ()
   , ssm_closeSessionManager :: IO ()
   }
data ClientSessionManager m conn sess st
   = ClientSessionManager
   { csm_base :: m (BaseSessionManager m conn sess st)
   , -- what do we need apart from that?
   }

All session values would then be wrapped with either newtype ServerSession sess = ServerSession sess and newtype ClientSession sess = ClientSession sess

The functions in https://github.com/agrafix/Spock/blob/138198fa5e50d7238bfecc64678403aadc80fc37/Spock/src/Web/Spock/SessionActions.hs would then move to type classes IsAnySession, IsServerSession and IsClientSession and be dependent on the session wrapper, as of how the session manager is initialized and chosen.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment