Using twitter to authenticate in Yesod

In this post I’m going to explain how you can use Twitter (OAuth 1.0a) to authenticate your users with Yesod (1.1).

Initial setup

I’m going to base this example on the scaffolded site. Create it by calling the “yesod” comnand with the “init” option and answer the usual questions.

$ yesod init

First add yesod-auth-oauth as a dependency in the .cabal file, for Yesod 1.1 its yesod-auth-oauth 1.1 so open the .cabal file and add this line to the “build-depends:” section:

, yesod-auth-oauth              >= 1.1        && < 1.2

Then run cabal install on your site directory to download and install the yesod-auth-oauth package dependencies.

 

Twitter setup

Assuming you have a twitter account, access https://dev.twitter.com/ and create an app (or just go directly to: https://dev.twitter.com/apps/new). Fill in your application details, and fill the “Callback URL” with a dummy url (use example.com or any url related to your app, yesod-auth-oauth is going to override this).

After your app is created a few additions are still necessary, so go to your apps page (https://dev.twitter.com/apps) and select your app from the list. Adjust your required access level, read-only is enough to authenticate, and take note of your consumer key and consumer secret, these are going to be used in Yesod. On this same page, click the blue button on the bottom to create “access tokens”. No need to take note of these.

 

Yesod setup

The yesod-auth-oauth package exports the module Yesod.Auth.OAuth. This module has two functions that are going to be of interest:

  • authTwitter, gets your consumer key and consumer secret and produces a AuthPlugin data type.
  • twitterUrl, the AuthR route parameter to use for twitter authentication.

In Foundation.hs add “import Yesod.Auth.OAuth (authTwitter)” to the imports. And add authTwitter to the authPlugins of the YesodAuth instance. It should look somewhat like this:

    authPlugins _ = 
        [ authGoogleEmail
        , authTwitter 
                 (encodeUtf8 "your consumer key goes here")
                 (encodeUtf8 "your consumer secret goes here")
        -- , more authentication backends here...
        ]

Here “encodeUtf8″ is a function that is part of Data.Text.Encoding and converts a Text string to a ByteString, which is the datatype needed by authTwitter.

You can then create a route to redirect your users whenever they are trying to access a page that they need to be authenticated and authorized. This is specified in the function “authRoute” of the Yesod instance in Foundation.hs. By default this function uses AuthR LoginR which is the equivalent of /login. Personally I found it hard to customize it and because of this I prefer to use my own route with a widget defined by me. If this is your case, define it as:

authRoute _ = Just LoginPanelR

Then create the LoginPanelR route in config/routes as:

/login LoginPanelR GET

This makes the /login address be handled by the “getLoginPanelR” function. Open Handlers/Home.hs and create this function and also import twitterUrl from Yesod.Auth.OAuth:

import Yesod.Auth.OAuth (twitterUrl)
getLoginPanelR :: Handler RepHtml
getLoginPanelR = defaultLayout $(widgetFile "login")

All requests to /login are going to be served with this handler, which is just the widget created by the template “login”. Now just use the “isAuthorized” function in your Yesod instance in Foundation.hs to define the routes that should redirect here:

    isAuthorized SpecialRouteR True = do
        mauth <- maybeAuth
  case mauth of
           Nothing -> return AuthenticationRequired
           Just (Entity _ user)
                | isAdmin user -> return Authorized
                | otherwise    -> unauthorizedI MsgNotAnAdmin

Creating the widget

The final step needed is to create the “login” templates of the widget used in “getLoginPanelR”. Create a new file called “login.hamlet” in the templates directory. This file should have the <form>’s necessary for your authentication plugins. In this example we are focusing on Twitter and also using Google as a possible authentication plugin. The login.hamlet file should look somewhat like this:

 

<section>
    <form method="get" action="@{AuthR forwardUrl}">
        <input type="hidden" name="openid_identifier" value="https://www.google.com/accounts/o8/id">
        <button value="Login via Google">
            Google
    <form method="get" action="@{AuthR twitterUrl}">
        <button value="Login via Twitter">
            Twitter

Notice the usage of  the “twitterUrl” function, this function temporarily forwards the user to twitter for authentication and then calls back your application with the credentials returned by twitter. The credentials are mapped to the datatype Creds in yesod-auth. The twitter auth plugin returns the string “twitter” in the “credsPlugin” field, and the twitter user id as the “credsIdent” field.

 

This is it, i hope this post is useful to you and can make your life easier :)

One thought on “Using twitter to authenticate in Yesod

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>