Servant subsite (with Persistent), with requireAuth working in handlers

70 views
Skip to first unread message

Luke Evans

unread,
Apr 8, 2020, 3:34:16 PM4/8/20
to Yesod Web Framework
I'm trying to get a Servant API integrated into Yesod as a subsite.

I can see a few resources online about this, specifically:
and

Now, I have a basic subsite working based on the first of these examples, where the Servant handlers just work without authorization, or any other need to interact with Yesod elements.
However, I'm now turning my attention to writing some handlers for some real APIs and will need therefore to call requireAuth, and possibly some other Yesod functions. 

Apparently, this will require a couple of things:
1. Using WaiSubsiteWithAuth to share the auth stuff in the subsite.  I've already modified the example code to do this.
2. Getting a MonadHandler instance established for the AppT m new type that wraps up all the Servant 'app' stuff.   This is of course the context in which things like requireAuth will work.

My questions:
a. Does this sound correct (i.e. is there likely to be anything else to do)?  Indeed, is there a preferred/better way to integrate Servant and Yesod (with Persistent)?
b. How should this MonadHandler instance actually be implemented in this case?  I'm not clear from reading the Yesod book.

Thanks!


rpuey...@gmail.com

unread,
Aug 23, 2020, 6:18:58 PM8/23/20
to Yesod Web Framework
Luke, how are you?

How did you to solve this problem. I have the same problem as you:

I was able to create a servant API as a Yesod subsite. I'm using Rethonk DB and I was able to access the bank data through the API. But...

I don't know exactly how to integrate this api with yesod authentication.

Rodolpho Pueyrredón

unread,
Aug 30, 2020, 3:11:27 PM8/30/20
to Yesod Web Framework
Hello Luke.

I made an api servant with authorization. I think you may have solved your problem by now, but I'll post it here for other people.

I started from the same base as your second example of how to make a servant api as a subsite in yesod


I have an EmbeddedAPI:


newtype EmbeddedAPI = EmbeddedAPI { eapiApplication :: Application }

instance RenderRoute EmbeddedAPI where
data Route EmbeddedAPI = EmbeddedAPIR ([Text], [(Text, Text)]) deriving(Eq, Show, Read)
renderRoute (EmbeddedAPIR t) = t

instance ParseRoute EmbeddedAPI where
parseRoute t = Just (EmbeddedAPIR t)

instance Yesod master => YesodSubDispatch EmbeddedAPI master where
yesodSubDispatch YesodSubRunnerEnv{..} req = resp
where
master = yreSite ysreParentEnv
site = ysreGetSub master
resp = eapiApplication site req

In the blog below, the author makes a subsite, but uses the WaiSubsite function. I simply adapted to use WaiSubsiteWithAuth.


My route looked like this:

-- By default this file is used by `parseRoutesFile` in Foundation.hs
-- Syntax for this file here: https://www.yesodweb.com/book/routing-and-handlers

-- API REST routes
/api/v1/products-manager    SubsiteR EmbeddedAPI appAPI

/static StaticR Static appStatic
/auth   AuthR   Auth   getAuth

/api/v1 HomeR GET

/api/v1/login UsersLoginR POST


I changed it to be like the blog, but putting WaiSubsiteWithAuth in place:

-- By default this file is used by `parseRoutesFile` in Foundation.hs
-- Syntax for this file here: https://www.yesodweb.com/book/routing-and-handlers

-- API REST routes
/api/v1/products-manager    SubsiteR WaiSubsiteWithAuth getServantWithAuth

/static StaticR Static appStatic
/auth   AuthR   Auth   getAuth

/api/v1 HomeR GET

/api/v1/login UsersLoginR POST


The '/api/v1/login' route is not subsite and I use it to authenticate.

In foundation I put the function getServantWithAuth as below:


getServantWithAuth :: App -> WaiSubsiteWithAuth
getServantWithAuth = WaiSubsiteWithAuth . eapiApplication . appAPI


eapiApplication is the Application field in EmbeddedAPI.


As a bonus, you can also see the example of this code that shows how to use JWT authentication:


I just didn't understand why he used the word 'token' when he took the token in the http header, in this excerpt:


extractToken :: Text -> Maybe Text
extractToken auth
| toLower x == "token" = Just $ dropWhile isSpace y
| otherwise = Nothing
where (x, y) = break isSpace auth

The header schema is when using JWT for example: "Bearer kgTvnjciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqd3QiOjF9.8oq_FD7t71bbdsf-DsVwpugtdfJUhTgba4W_GEdknSc".

So change like this:


extractToken :: Text -> Maybe Text
extractToken auth
| toLower x == "bearer" = Just $ dropWhile isSpace y
| otherwise = Nothing
where (x, y) = break isSpace auth


It all works.

Em domingo, 23 de agosto de 2020 19:18:58 UTC-3, Rodolpho Pueyrredón escreveu:

Luke Evans

unread,
Sep 3, 2020, 11:54:12 PM9/3/20
to Yesod Web Framework
Hey, thanks for that.  I had kind of let that project wither on the vine a bit as the Servant API in Yesod was really just an adventure - and I had my API all implemented with regular Yesod handlers (and limited time to experiment). 

However, your detailed post has given me some impetus to return to it and try again.  I really appreciate you taking the trouble to write up your work.

Reply all
Reply to author
Forward
0 new messages