Haskell Servant passing custom data to auth handler

598 views Asked by At

I'm using custom monad (with reader) to easily pass data like DB pool to my handlers (before using custom monad I used to pass connection as fn argument).

This is how I've defined my custom monad :

newtype Controller a = Controller
    { runController :: ReaderT ServerEnvironment Handler a
    } deriving ( Functor, Applicative, Monad, MonadReader ServerEnvironment, 
                 MonadError ServantErr, MonadIO )

This ServerEnvironment is just custom datatype I use to carry my data.

Problem is that for my AuthHandler I have to specifically use function with:

r -> Handler usr

as authentication handler, I can't use my custom handler which would be :

r -> Controller usr

and I also have no way to pass in my ConnectionPool because signature can't be :

ConnPool -> r -> Handler usr

So, how does one pass extra data to authentication handler in servant without using global IO state?

1

There are 1 answers

5
user2141650 On BEST ANSWER

The AuthHandler you put into the context doesn't have to be defined at the top-level! Generally, you'll want to do it in main so you have access to the database connections etc. which you've created:

type API = 
  ... :<|> (AuthProtect "myProtection" :> ...) :<|> ...

type instance AuthServerData (AuthProtect "myProtection") = User

server :: ServerEnvironment -> Server API
server env = ...

setupEnv :: IO ServerEnvironment
setupEnv = ..

-- This is essentially a 'Controller'.
authenticate :: ServerEnvironment -> Handler User
authenticate conn = ...

main :: IO ()
main = do
  env <- setupEnv
  -- Now, because we have access to the env, we can turn our
  -- 'authenticate' into the right type before putting it
  -- in the context
  let ctx = authenticate env :. EmptyContext
  run 8080 $ serveWithContext myAPI (server conn) ctx