Tuesday, June 28, 2011

Implementing jQuery with Facebook Graph API for OAuth 2.0

I spent a couple of days to try to implement Facebook in my website. Although I had an experience to integrate with Facebook, I still found it is not so easy to be integrated with, especially after they forced people to use OAuth 2.0 as part of the process (as at Jun 2011).


Requirement:
- retrieve my recent facebook photos without my login and display them whoever come to my website.
- not to expose any Facebook secret key information to end user

Expected flow:

Process:
In order to do this, first, I have to sign up a facebook app in the developer website. I filled in all the detail (e.g. WEBSITE_URL) for my website, then facebook will generate an App Id, API key and App secret. For my requirement, I only need the App Id and App secret. And since this app will only be used by myself and I don't have to submit it and get published for other people.

Once I've got the above details, I can create a request to facebook which will generate an access token for myself. An access token is needed when you want to fetch facebook information except user basic detail. i.e. you don't need access token for user detail like "http://graph.facebook.com/angus.newgen".

If you follow what described in Facebook authentication document, you will see that it needs to go around the circle 3 times between facebook and your website in order to get access to the facebook data that you need. Basically, what the document for server-side flow described are:

1. Get the {CODE} from facebook. The {CODE} is needed to make sure the website (redpossum, in this case) can access end user's facebook information based on permissions.
2. Once the facebook confirmed that the website can access end user's account, it then generated a {CODE} and make a redirect call back to the website for the next request.
3. The website will then request for an access token based on the {CODE}, App Id and App Secret.
4. Once facebook generated an access token and passed back to the website, the website can request those authorised (based on end user's permissions) end user's information, for example, the following url will get my feed data.
https://graph.facebook.com/angus.newgen/feed?access_token={ACCESS_TOKEN}

In step 2 and 3, facebook is using redirect mechanism to get the end user request for the token again. It doesn't fit the way I am expecting an ajax callback for jQuery. That's because the initial call which is to get the {CODE} from jQuery will be lost once it hits facebook.



In fact, by looking at client-side approach, it is using redirect mechanism too. So both of the sample code are not fit of what I want. 

Hence, the only way I can do is to manually generate the access token instead of requesting on runtime so that I could avoid the redirect call from Facebook.

Before I go further to the solution, I want to mention about the importance of permission. As you can see from the steps above, I emphasize a few times about permissions is because certain information can only be accessible when user authorised them. For example, if the website want to access my photo album, I need to authorise the website with "user_photo" permission. In order to give the website authorisation, the website need to put a "scope" parameter when it requests for the {CODE} in step 1 above. So the request URL will look like
https://www.facebook.com/dialog/oauth?client_id={APP_ID}
      &redirect_uri=http://www.redpossum.com
      &scope=user_photo

For further information about permission and see what can be authorised, please read the reference document.

In addtion, asking for "user_photo" permission is not enough to fulfill the requirement. As the {CODE} generated by the previous URL will expire within a few hours. So you need another permission "offline_access" in order to get a lifetime access token. The lifetime access token will never expire unless I change my password.

So I have to manually put the following URL in order to get the lifetime {CODE} with photo access permission:

https://www.facebook.com/dialog/oauth?client_id={APP_ID}
      &redirect_uri=http://www.redpossum.com
      &scope=user_photo,offline_access

Once I got the lifetime {CODE}, then I will manually put the following URL in order to get the lifetime access token:
https://graph.facebook.com/oauth/access_token?client_id={APP_ID}
      &redirect_uri=http://www.redpossum.com
      &client_secret={APP_SECRET}&code={CODE}
Now I've got the lifetime access token, I can access my facebook album use the following request:

https://graph.facebook.com/me/albums?access_token={ACCESS_TOKEN}
However, this result only gives me a list of my photo album, but no photos information in an album. Therefore, in order to get the individual photo in an album, I have to make another call based on the {ALBUM_ID} which I've got from the previous result.

https://graph.facebook.com/{ALBUM_ID}/photos?access_token={ACCESS_TOKEN}
The following is the server side code written in PHP. It returns a json with album title, created date and a list of Image object:

<?php
  class Image {
    public $name;
    public $thumbnail;
    public $large_image;
  }

  // APP_ID
  $facebook_id = "..."; $access_token = "...";
  $albums_limit = 1;
  $photos_limit = 10;

  $albums_url = "https://graph.facebook.com/" . $facebook_id
       . "/albums?limit=" . $albums_limit . "&access_token="
       . $access_token;
  $album_data = json_decode(file_get_contents($albums_url));

  $album_id = $album_data->data[0]->id;

  $photos_url = "https://graph.facebook.com/" . $album_id
       . "/photos?limit=" . $photos_limit . "&access_token="
       . $access_token;
  $photos_data = json_decode(file_get_contents($photos_url));

  $photos_array = array();
  foreach ($photos_data->data as $entry) {
      $image = new Image();
      $image->name = $entry->name;
      $image->thumbnail = $entry->images[3]->source;
      $image->large_image = $entry->source;
      $photos_array[] = $image;
  };

  $json_array = array(
            'title'=>$album_data->data[0]->name,
            'created_time'=>$album_data->data[0]->created_time,
              'images'=>$photos_array);

  echo json_encode($json_array);

?>
And this is the jQuery code that retrieve the facebook data out of the above PHP.

$.getJSON("facebook.php", function(data) {
  $("#title").append(data.title);
  $("#date").append(data.created_time);

  $.each(data.images, function(key, json) {
    var content = "<div class="image">";
    content += "<a href='" + json.large_image + "'>";
    content += "<img alt='"+ json.name + "' src='"
       + json.thumbnail + "'/>";
    content += "</a>";
    content += "</div>";
    $("#data").hide().append(content);
  });

  $("#data").show();
});
Finally you can see the actual result from my website: http://www.redpossum.com

No comments:

Post a Comment