-
Notifications
You must be signed in to change notification settings - Fork 67
Happstack Facebook: Under Construction
When developing a web application, the developers have the opportunity to connect with the social networking website Facebook. This can allow users to option to provide the web application with information that they have shared with Facebook, and allow the web application to interact with the user's Facebook profile. This tutorial explores how to accomplish some of these tasks using the Haskell fb library. This tutorial will not explain Facebook's Graph API, but rather explain how to interact with the Graph API using fb.
For this guide, you will need to install fb
, and you will need a Facebook application.
Let's get started by jumping into an example that will request your user to authenticate your application.
This example will demonstrate how to redirect a user to Facebook to authorize the use of our application. This is the first step in the oAuth procedure. The user will authorize the application through Facebook, and then Facebook will redirect the user to a specific URL in our application with a query parameter that is a unique code. This code will then be used to retrieve a user access token, which will allow us to access the user's Facebook information.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Happstack.Server
import qualified Facebook as FB
import qualified Data.ByteString.Char8 as BS
import Network.HTTP.Conduit (withManager)
import qualified Data.Text as T
app :: FB.Credentials
app = FB.Credentials "localhost" "INSERT_APP_ID" "INSERT_SECRET"
url :: FB.RedirectUrl
url = "http://localhost:8000/get-email"
perms :: [FB.Permission]
perms = ["user_about_me", "email"]
retrieveAuthURL :: IO String
retrieveAuthURL = withManager $ \manager -> FB.runFacebookT app manager $ do
fbAuthUrl <- FB.getUserAccessTokenStep1 url perms
return $ T.unpack fbAuthUrl
testString :: String
testString = "test-string"
main :: IO ()
main = do
redirectStr <- retrieveAuthURL
simpleHTTP nullConf $ seeOther redirectStr $ toResponse testString
Every Facebook application has an ID and a secret. It is imperative that you do not share your secret with anybody other than the developers of the application. Think of the secret as the password for the application. The ID and the secret must be inserted here in order for Facebook to verify that your application is making the request.
This variable represents a list of the permissions that your application is requesting.
The url is the URL that the user will be redirected to once they have authorized your application.
withManager
keeps connections within the call to withManager
alive with something called a Manager
. It is likely that the Facebook action that is being performed will send a request to Facebook and wait for a response, and as such we need to keep the connection alive.
The type declaration of withManager
is ResourceIO m => (Manager -> ResourceT m a) -> m a
. withManager
provides a Manager
to the function ResourceIO m => (Manager -> ResourceT m a)
and releases it once the function has finished. It then outputs a ResourceIO m => m a
.
FB.runFacebookT runs a computation in the FacebookT monad transformer with the provided credentials. This means that FB.runFacebookT runs desired actions with a connection to Facebook with your application's credentials (ID and secret) so that actions such as Graph API queries can be performed.
The type declaration of FB.runFacebookT is :: Credentials -> Manager -> FacebookT Auth m a -> m a
. The first parameter is the credentials for the application, as the above example does with app
. The second parameter is a Manager
, as the above example does with withManager
. The third parameter is a computation that outputs something of type FacebookT Auth m a
.
FB.getUserAccessTokenStep1 creates the URL that the user is sent to, and is generated from the RedirectUrl and the Permissions. The type of FB.getUserAccessTokenStep1 is Monad m => RedirectUrl -> [Permission] -> FacebookT Auth m Text
. Notice that we can run this in the FB.runFacebookT function as FB.getUserAccessTokenStep1 outputs something of type FacebookT Auth m Text
. The function FB.getUserAccessTokenStep1 also takes in a list of permissions (perms
) and a RedirectUrl (url). The url must be under the registered domain in your Facebook application, and is where Facebook will redirect your user once they have authenticated your application.
FB.runFacebookT runs FB.getUserAccessTokenStep1, and pulls the Text object from the FacebookT Auth m Text object. This is then returned as an IO String, which is used in the main method when the server starts. Upon visiting a specified route, the user will be redirected to this URL (the Facebook URL, not the redirect URL) using seeOther
.
Now that the user has authenticated your application, let's retrieve some of the information that they have authenticated our application to retrieve.
Let's go through an example that will display the user's email in the browser.
The code would look something like this:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Happstack.Server
import qualified Facebook as FB
import qualified Data.ByteString.Char8 as BS
import Network.HTTP.Conduit (withManager)
import qualified Data.Text as T
import Control.Monad.IO.Class (liftIO)
import Control.Monad (msum)
args :: String -> FB.Argument
args code = ("code", BS.pack code)
app :: FB.Credentials
app = FB.Credentials "localhost" "INSERT_APP_ID" "INSERT_SECRET"
url :: FB.RedirectUrl
url = "http://localhost:8000/get-email"
perms :: [FB.Permission]
perms = ["user_about_me", "email"]
testString :: String
testString = "test-string"
main :: IO ()
main = do
x <- retrieveAuthURL
simpleHTTP nullConf $ msum [ dir "auth" $ seeOther x $ toResponse testString,
dir "get-email" $ look "code" >>= getEmail
]
getEmail :: String -> ServerPart Response
getEmail code = liftIO $ retrieveFBData code
retrieveAuthURL :: IO String
retrieveAuthURL = withManager $ \manager -> FB.runFacebookT app manager $ do
fbAuthUrl <- FB.getUserAccessTokenStep1 url perms
return $ T.unpack fbAuthUrl
retrieveFBData :: String -> IO Response
retrieveFBData code = withManager $ \manager -> FB.runFacebookT app manager $ do
token <- FB.getUserAccessTokenStep2 url [args code]
user <- FB.getUser "me" [] (Just token)
return $ toResponse (FB.userEmail user)
FB.getUserAccessTokenStep2 is similar to the aforementioned FB.getUserAccessTokenStep1. This step will send the 'code' that was retrieved in the URL query parameters from FB.getuserAccessTokenStep1 to Facebook and receive the user access token as the response.
The type declaration of FB.getUserAccessTokenStep2 is ResourceIO m => RedirectUrl -> [Argument] -> FacebookT Auth m UserAccessToken
. This is, again, similar to FB.getUserAccessTokenStep1. The first parameter is the RedirectUrl, and is exactly the same as the URL the was put into the call to FB.getUserAccessTokenStep1. The second parameter are the query parameters which include the 'code' parameter that was fetched from FB.getUserAccessTokenStep1. The only item of the [Argument] parameter should be a list with one tuple that contains the string "code" as the first item of the tuple, and the actual code as the second item. FB.getUserAccessTokenStep2 then outputs the user access token, if successful. Huzzah!
FB.getUser is another action that produces something of type FacebookT Auth m a
, where a
happens to be a User
in this instance. Our above function extracts the User
object from the action.
The type declaration for FB.getUser is UserId -> [Argument] -> Maybe UserAccessToken -> FacebookT Auth m User
. The first parameter, UserId
, can be any user's Facebook ID. The use of "me" can be found on the Facebook Developer website. The second parameter is a list of the arguments that you want to pass to the Facebook Graph API. The third argument is the user access token that was previously retrieved.
FB.userEmail simply returns the input user's email as Text
. Other attributes of users can be found here.