packages feed

prodapi-userauth-0.1.0.0: src/Prod/UserAuth/HandlerCombinators.hs

module Prod.UserAuth.HandlerCombinators (
    authServerContext,
    authorized,
    limited,
    withLoginCookieVerified,
    withOptionalLoginCookieVerified,
    Request,
)
where

import Data.Maybe (isJust)
import Data.Text.Encoding (decodeLatin1)
import Network.Wai (Request, requestHeaders)
import Prod.UserAuth.Base
import Prod.UserAuth.JWT
import Prod.UserAuth.Runtime
import Servant
import Servant.Server
import Servant.Server.Experimental.Auth (
    AuthHandler,
    mkAuthHandler,
 )
import Web.Cookie (parseCookies)

authHandler :: Runtime a -> AuthHandler Request UserAuthInfo
authHandler runtime = mkAuthHandler go
  where
    go req = do
        let mCookies = fmap parseCookies $ lookup "cookie" $ requestHeaders req
        let jwtblob = fmap decodeLatin1 $ lookup "login-jwt" =<< mCookies
        let mJwt = decodeAndVerifySignature (toVerify . hmacSecret $ secretstring runtime) =<< jwtblob
        traceJWT runtime mJwt
        pure $ UserAuthInfo mJwt

authServerContext :: Runtime a -> Context (AuthHandler Request UserAuthInfo ': '[])
authServerContext runtime =
    authHandler runtime :. EmptyContext

withLoginCookieVerified ::
    Runtime info ->
    Maybe LoggedInCookie ->
    (JWT VerifiedJWT -> Handler a) ->
    Handler a
withLoginCookieVerified runtime Nothing _ = do
    traceVerification runtime False
    throwError $ err403{errBody = "Sorry, need some cookies."}
withLoginCookieVerified runtime cookie act = do
    withOptionalLoginCookieVerified runtime cookie $ \mJwt ->
        maybe
            (traceVerification runtime False >> throwError err403)
            (\x -> traceVerification runtime True >> act x)
            mJwt

withOptionalLoginCookieVerified ::
    Runtime info ->
    Maybe LoggedInCookie ->
    (Maybe (JWT VerifiedJWT) -> Handler a) ->
    Handler a
withOptionalLoginCookieVerified runtime cookie act = do
    let mJwt = decodeAndVerifySignature (toVerify . hmacSecret $ secretstring runtime) =<< fmap encodedJwt cookie
    traceOptionalVerification runtime (isJust mJwt)
    act mJwt

authorized :: Runtime info -> UserAuthInfo -> (UserId -> Handler a) -> Handler a
authorized rt auth act =
    limited rt auth (traceDisallowed rt >> throwError err401) act

limited :: Runtime info -> UserAuthInfo -> (Handler a) -> (UserId -> Handler a) -> Handler a
limited rt auth fallback act =
    maybe (traceLimited rt >> fallback) (\uid -> traceAllowed rt >> act uid) (authUserId auth)