Sunday, January 16, 2011

Facebook OAuth Example

In this example, we will build a barebones AppEngine application that uses Facebook for login. The complete source code for this example can be found in examples/oauth directory located at facebook python library code.

You can see the example in action at Facebook OAuth Demo.

We will use OAuth 2.0 directly rather than relying on Facebook’s JavaScript SDK for login. It also accesses the Facebook Graph API directly rather than using the Python SDK. It is designed to illustrate how easy it is to use the Facebook Platform without any third party code.

Using JavaScript is recommended if it is feasible for your application, as it handles some complex authentication states that can only be detected in client-side code.

Before you begin this tutorial, you need to have a facebook application registered and keep its ApplicationID and Application Secret handy with you.

FACEBOOK_APP_ID = "your app id" FACEBOOK_APP_SECRET = "your app secret" 

User Model

We will create a user model class because we need to store user’s information once he signs up with our web application. Here is an example User Model Class for AppEngine data store.

class User(db.Model):
"""User Model Class"""
  id = db.StringProperty(required=True) #facebook user-id
  created = db.DateTimeProperty(auto_now_add=True)
  updated = db.DateTimeProperty(auto_now=True)     
  name = db.StringProperty(required=True)     
  profile_url = db.StringProperty(required=True)     
  access_token = db.StringProperty(required=True)  #fb OAUTH access token 

Facebook Authentication

This is the most important section of the tutorial where we handle Facebook Authentication functionality. The user login flow works like this:

  • When user clicks on login button, we redirect user to Facebook Authorize URLwhere he is shown Facebook Login Screen and then Facebook Permission Screen. It is very easy to construct authorize url. It is Facebook OAuth URL with a few additional parameters; client_id and redirect_url. There are other parameters as well related to permissions and display style etc. but are not needed for this example. So in our case, following lines does the magic.

    args = dict(client_id=FACEBOOK_APP_ID, redirect_uri=self.request.path_url) 
    self.redirect("https://graph.facebook.com/oauth/authorize?" + urllib.urlencode(args)) 

    The first line assigns args to a python dict which contains client_id as your app_idand redirect_uri points to /login URL.

  • Second Step of the process is, when a user authorizes our Facebook Application and grants necessary permissions. Facebook OAuth Server redirects user to redirect_uriURL specified in previous step with an additional argument code. If you look at the code closly, both of these steps are handled by same method get of LoginHandlerclass. The presence of code parameter in the request differentiate the first step from the second. Now once we detect the user has granted access to our Application, we request for access_token like this.

    args = dict(client_id=FACEBOOK_APP_ID, redirect_uri=self.request.path_url)
    """redirect_url points to */login* URL of our app""" 
    args["client_secret"] = FACEBOOK_APP_SECRET  #facebook APP Secret
    args["code"] = self.request.get("code") 
    response = cgi.parse_qs(urllib.urlopen("https://graph.facebook.com/oauth/access_token?" +
       urllib.urlencode(args)).read())
    access_token = response["access_token"][-1] 

    Note that request_uri parameter needs to be same in both the steps of constructingauth URL as well as access_token request.

  • Now once we have facebook access token, we can make calls to Graph API and get required information for user and his connections etc and save in database. You can use Facebook Python library’s objects method get_object with id as “me” or call the Graph API’s URL directly.

    profile = json.load(urllib.urlopen(
      "https://graph.facebook.com/me?" + 
      urllib.urlencode(dict(access_token=access_token))))
    """in above step, profile object gets basic profile information about the 
    facebook user. 
    In the step below, User Model Class is initialized with profile 
    information and saved in db. """ 
    user = User(key_name=str(profile["id"]),
        id=str(profile["id"]),
        name=profile["name"],
        access_token=access_token,
        profile_url=profile["link"])
    user.put() 

Session Management and Logging out

Once user is authenticated and his basic profile has been created in our app, we want to identify the user in every request henceforth and personalize our application for this user. We use browser cookie to maintain session state. It involves two steps explained below:

  • Setting Cookie: As soon as we create or update user profile information in previous step, we set browser cookie containing facebook user-id of the user in a secured way. Facebook App Secret is used in generating cookie content’s digest and simple encoding scheme is used to encode the cookie content. This is achieved through cookie_signature and set_cookie function in the code. Following line is the top level code setting cookie.

    set_cookie(self.response, "fb_user", str(profile["id"]), expires=time.time() + 30 * 86400) 
  • Parsing Cookie: Every Request, we parse the cookies in the request to detect logged in user. Please refer to parse_cookie method for details of parsing cookie information. The top level code which detect if a user is present and set current_user property of the base controller.

    def current_user(self):     
    """Returns the logged in Facebook user, or None if unconnected."""
     if not hasattr(self, "_current_user"):
      self._current_user = None
              user_id = parse_cookie(self.request.cookies.get("fb_user"))
              if user_id:
                    self._current_user = User.get_by_key_name(user_id)
       return self._current_user 
  • Logging Out:* Logging out a user is pretty simple. Just delete the browser cookie and user will no longer be detected as logged-in. Following code does that.

    set_cookie(self.response, "fb_user", "", expires=time.time() - 86400) 

Thats it! Hope that the article was useful. You can see the example in action at Facebook OAuth Demo.