Reputation: 1343
Has anyone used IHP for an app backend and if so, what changes need to be made for that to be doable? Is there a jwt package or something like it that allows IHP to have JWT authentication? Does Digitally Induced support using IHP as an app backend?
Upvotes: 0
Views: 78
Reputation: 1396
IHP itself has JWT only as as part of DataSync right now. But here's a custom JWT implementation used at digitally induced:
{-# LANGUAGE AllowAmbiguousTypes #-}
module Application.Auth (initJWTAuthentication) where
import IHP.Prelude
import IHP.LoginSupport.Helper.Controller
import IHP.Controller.Session
import IHP.QueryBuilder
import IHP.Fetch
import IHP.ControllerSupport
import IHP.ModelSupport
import IHP.Controller.Context
import IHP.Controller.Param
import qualified Web.JWT as JWT
import qualified Data.ByteString as BS
import qualified Data.Maybe as Maybe
import qualified Data.Text as Text
{-# INLINE initJWTAuthentication #-}
initJWTAuthentication :: forall user normalizedModel.
( ?context :: ControllerContext
, ?modelContext :: ModelContext
, normalizedModel ~ NormalizeModel user
, Typeable normalizedModel
, Table normalizedModel
, FromRow normalizedModel
, PrimaryKey (GetTableName normalizedModel) ~ UUID
, GetTableName normalizedModel ~ GetTableName user
, FilterPrimaryKey (GetTableName normalizedModel)
, KnownSymbol (GetModelName user)
) => IO ()
initJWTAuthentication = do
let accessTokenQueryParam = (paramOrNothing "access_token")
let accessToken :: Maybe Text = accessTokenQueryParam <|> jwtFromAuthorizationHeader
case accessToken of
Just accessToken -> do
signer <- fromContext @JWT.Signer
let signature = JWT.decodeAndVerifySignature signer accessToken
case signature of
Just jwt -> do
let userId :: Id user = jwt
|> JWT.claims
|> JWT.sub
|> Maybe.fromMaybe (error "JWT missing sub")
|> JWT.stringOrURIToText
|> textToId
user <- fetchOneOrNothing userId
putContext user
Nothing -> error "Invalid signature"
Nothing -> pure ()
jwtFromAuthorizationHeader :: (?context :: ControllerContext) => Maybe Text
jwtFromAuthorizationHeader = do
case getHeader "Authorization" of
Just authHeader -> authHeader
|> cs
|> Text.stripPrefix "Bearer "
|> Maybe.fromMaybe (error "Invalid format of Authorization header, expected 'Bearer <jwt>'")
|> Just
Nothing -> Nothing
Drop that into Application/Auth.hs
. Then change Web/FrontController.hs
to call the initJWTAuthentication
function like this:
instance InitControllerContext WebApplication where
initContext = do
setLayout defaultLayout
initAuthentication @User
initJWTAuthentication @User
After that you can make API requests to your IHP app like http://myapp/SomeAction?access_token=<JWT here>
or by setting the Authorization
HTTP header like Authorization: Bearer <JWT here>
Upvotes: 1