Posts Tagged ‘Facebook’

Facebook with Hunchentoot and OAuth2

June 28, 2012

This is a quick and dirty to see how you can access Facebook stuff from Hunchentoot.

You will need Hunchentoot, Drakma, cl-who and cl-json to run this code. All of them can be gotten with Quicklisp.

You will also need a valid domain that points to localhost to test this code on your local machine. I just set up a subdomain on one of my servers that points to 127.0.0.1. If you don’t have access to your own domain server use localtunnel.com or something like it. What ever domain you decide on remember to set the site url in your Facebook app settings to the same domain.

When you want to access Facebook resources you need an access token and to get an access token you have to take the user to a facebook page that asks the user to log in and to give access to your app. Once the user has given access or denied access facebook redirects the user back to the callback uri that you specified, bringing them back to your web page.

If you look at the code you will see that we first get an access code from /dialog/oauth and then a token from /oauth/access_token, this is because we are using response_type=code. The reason we don’t use response_type=access_token is because what Facebook returns can’t be accessed by hunchentoot only javascript can get hold of the stuff after the # that is returned where you would expect a querystring.

Once we have an access token we fetch trivial data form Facebook for the user that authenticated.

The following code is not pretty but it is as short as I could get it without confusing the issues at hand.

There are various OAuth implementations for Common Lisp but if you are reading this you might have struggled with those and hopefully this code will help to clarify what is going on in those more involved implemetations of OAuth. (And none of them had Facebook examples that I could find.)

(define-easy-handler (callback :uri "/callback") ()
  (if (not (parameter "code"))
    (with-html-output-to-string (*standard-output*)
      ;;Clicking on this link will get you an access code, if the user approves.
      (:a :href "https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&response_type=code
                  &redirect_uri=http://A_VALID_DOMAIN_THAT_POINTS_TO_LOCALHOST:8000/callback" 
               (str "Go Facebooking")))
    (multiple-value-bind (body status)
        ;;Use the access code to get a access token.
        (drakma:http-request 
         (format nil "https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID
                       &redirect_uri=http://A_VALID_DOMAIN_THAT_POINTS_TO_LOCALHOST:8000/callback
                       &client_secret=APP_SECRET=~A" 
                 (parameter "code")))

      ;;Check if all was well.
      (if (equal status 200)
          (let* ((querystring (split-string body #\&))
                 (access-token (split-string (first querystring) #\=))
                 ;;(expiry (split-string (second querystring) #\=))
                 )

            ;;Fetch something interesting with your access token.
            (multiple-value-bind (bodyx)
                (drakma:http-request 
                 (format nil "https://graph.facebook.com/me?access_token=~A" 
                         (second access-token)))
                (format nil "~A" (json::decode-json-from-string bodyx))))
          (format nil "Something went wrong:~%~A"
                  (json::decode-json-from-string body))))))

(defun split-string (string char)
    "Returns a list of substrings of string divided by char."
    (loop for i = 0 then (1+ j)
          as j = (position char string :start i)
          collect (subseq string i j)
          while j))