Yesod admin auth with BrowserId and the scaffolded website

197 views Asked by At

I'm trying to do a very simple app with Yesod (a blog app) following https://www.youtube.com/watch?v=SadfV-qbVg8 (I used the scaffolded website)

I want to add a simple authentification to secure the access of the creation of an article.

Following http://www.yesodweb.com/book/authentication-and-authorization, I added:

-- Routes not requiring authentication.
isAuthorized (AuthR _) _ = return Authorized
isAuthorized FaviconR _ = return Authorized
isAuthorized RobotsR _ = return Authorized
isAuthorized PublishArticleR _ = isAdmin
-- Default to Authorized for now.
isAuthorized _ _ = return Authorized

My new route is PublishArticleR. The isAdmin function is the same as in the book:

isAdmin = do
    mu <- maybeAuthId
    return $ case mu of
        Nothing -> AuthenticationRequired
        Just "admin" -> Authorized
        Just _ -> Unauthorized "You must be an admin"

And it doesn't compile :(

Foundation.hs:76:38:
    No instance for (IsString UserId) arising from a use of ‘isAdmin’
    In the expression: isAdmin
    In an equation for ‘isAuthorized’:
        isAuthorized PublishArticleR _ = isAdmin
    In the instance declaration for ‘Yesod App’

I don't understand what I'm doing wrong…

Thanks,

EDIT:

More information about my AuthId, it's defined like this:

type AuthId App = UserId

My model is:

User
    ident Text
    password Text Maybe
    UniqueUser ident
    deriving Typeable

And I want to check if the ident property is equal to something (like my email address for example) to authorize to publish new articles.

1

There are 1 answers

4
JP Moresmau On

maybeAuthId will return an AuthId object if the user is authenticated. In the example from the yesod book, AuthId is only a synonym for Text: it's only a user name. Text objects (and other types that have IsString instances) can be built from string literals, which is why the example code works: Haskell knows how to transform

"admin"

into a Text object.

You're using a more complex type to represent a logged in User, so you either need to provide a IsString instance for User (which will build a user without a password, say):

instance IsString User where
   fromString s = User (pack s) ""

Or, maybe easier, modify your isAdmin function to get the ident part of your User object, something like:

 isAdmin = do
   mu <- maybeAuthId
   return $ case mu of
     Nothing -> AuthenticationRequired
     Just (User ident _) -> case ident of
       "admin" -> Authorized
       _ -> Unauthorized "You must be an admin"

EDIT I misread your definition of AuthId, I thought it was

type AuthId App = User

In fact, what you have is UserID, which are the IDs of your User objects in your database. So you can do two things: precompute the list of IDs of users that have admin privilege and see if the user id maybeAuthId gives you is one of them, or read the User in the db from the given ID and see if he has the rights...