{- mimic server side session store -} module Session where import Control.Concurrent.MVar import Control.Monad.IO.Class import Control.Monad.Trans.Except import Data.Default import Data.Map.Strict qualified as Map import Data.Text.Lazy qualified as TL import Network.OAuth2.Experiment import Network.OAuth2.Provider import Types -- For the sake of simplicity for this demo App, -- I store user data in MVar in server side. -- It means user session shared across browsers. -- which simplify my testing cross browsers. -- I am sure you don't want to this for your production services. initUserStore :: [IdpName] -> IO AuthorizationGrantUserStore initUserStore = do fmap AuthorizationGrantUserStore . newMVar . Map.fromList . fmap (\idpName -> (idpName, def {idpName = idpName})) insertCodeVerifier :: AuthorizationGrantUserStore -> IdpName -> Maybe CodeVerifier -> ExceptT TL.Text IO () insertCodeVerifier store idpName val = do sdata <- lookupAppSessionData store idpName let newData = sdata {authorizePkceCodeVerifier = val} liftIO $ upsertAppSessionData store idpName newData upsertAppSessionData :: AuthorizationGrantUserStore -> IdpName -> IdpAuthorizationCodeAppSessionData -> IO () upsertAppSessionData (AuthorizationGrantUserStore store) idpName val = do m1 <- takeMVar store let m2 = if Map.member idpName m1 then Map.adjust (const val) idpName m1 else Map.insert idpName val m1 putMVar store m2 allAppSessionData :: AuthorizationGrantUserStore -> IO [IdpAuthorizationCodeAppSessionData] allAppSessionData (AuthorizationGrantUserStore store) = do m1 <- tryReadMVar store return $ maybe [] Map.elems m1 removeAppSessionData :: AuthorizationGrantUserStore -> IdpName -> IO () removeAppSessionData store idpName = do upsertAppSessionData store idpName (def {idpName = idpName}) lookupAppSessionData :: AuthorizationGrantUserStore -> IdpName -> ExceptT TL.Text IO IdpAuthorizationCodeAppSessionData lookupAppSessionData (AuthorizationGrantUserStore store) idpName = do mm <- liftIO $ tryReadMVar store m1 <- except $ maybe (Left "[lookupAppSessionData] store (mvar) is empty") Right mm except $ maybe (Left $ "[lookupAppSessionData] unable to find cache data for idp " <> toText idpName) Right (Map.lookup idpName m1)