module PostgREST.Auth (
setRole
, claimsToSQL
, jwtClaims
, tokenJWT
) where
import Control.Monad (join)
import Data.Aeson (Value (..), Object)
import Data.Aeson.Types (emptyObject, emptyArray)
import Data.Vector as V (null, head)
import Data.Map as M (fromList, toList)
import Data.Monoid ((<>))
import Data.String.Conversions (cs)
import Data.Text (Text)
import Data.Time.Clock (NominalDiffTime)
import PostgREST.QueryBuilder (pgFmtLit, pgFmtIdent, unquoted)
import qualified Web.JWT as JWT
import qualified Data.HashMap.Lazy as H
claimsToSQL :: JWT.ClaimsMap -> [Text]
claimsToSQL = map setVar . toList
where
setVar ("role", String val) = setRole val
setVar (k, val) = "set local postgrest.claims." <> pgFmtIdent k <>
" = " <> valueToVariable val <> ";"
valueToVariable = pgFmtLit . unquoted
jwtClaims :: JWT.Secret -> Text -> NominalDiffTime -> Maybe JWT.ClaimsMap
jwtClaims secret input time =
case join $ claim JWT.exp of
Just expires ->
if JWT.secondsSinceEpoch expires > time
then customClaims
else Nothing
_ -> customClaims
where
decoded = JWT.decodeAndVerifySignature secret input
claim :: (JWT.JWTClaimsSet -> a) -> Maybe a
claim prop = prop . JWT.claims <$> decoded
customClaims = claim JWT.unregisteredClaims
setRole :: Text -> Text
setRole role = "set local role " <> cs (pgFmtLit role) <> ";"
tokenJWT :: JWT.Secret -> Value -> Text
tokenJWT secret (Array a) = JWT.encodeSigned JWT.HS256 secret
JWT.def { JWT.unregisteredClaims = fromHashMap o }
where
Object o = if V.null a then emptyObject else V.head a
fromHashMap :: Object -> JWT.ClaimsMap
fromHashMap = M.fromList . H.toList
tokenJWT secret _ = tokenJWT secret emptyArray