Ramblings of Daniel Graziotin

Handle Facebook OAuth authentication with Python SDK

H

I took the occasion of leaving Facebook to write a Python program to inform my Facebook friends on how to contact me after my farewell. Therefore, I learnt something new.
I would like to share with you how to manage Facebook OAuth steps using Python SDK, because I could not find a suitable well-written example on the Internet.

Let’s take a look at my access.py file. I stripped down some details in this post:

import facebook
import urllib
import warnings
import webbrowser
import dbmanager
import urlparse
import settings
import os


class FBOAuth(object):
    """
    Handles the OAuth phases of Facebook and returns an authenticated GrapApi object, to be used for
    using the Facebook API.
    """
    # the following values are taken from settings.py file but could be hardcoded here
    FACEBOOK_GRAPH_URL = settings.FACEBOOK_GRAPH_URL
    CLIENT_ID     = settings.CLIENT_ID
    CLIENT_SECRET = settings.CLIENT_SECRETS
    REDIRECT_URI = settings.REDIRECT_URI

    SECRET_CODE = None
    ACCESS_TOKEN = None

    def __init__(self):
        self.database = dbmanager.DBManager()
        FBOAuth.SECRET_CODE = self.get_secret_code()
        FBOAuth.ACCESS_TOKEN = self.get_access_token()

    def authorize(self):
        
        warnings.filterwarnings('ignore', category=DeprecationWarning)
        savout = os.dup(1)
        os.close(1)
        os.open(os.devnull, os.O_RDWR)
        try:
            webbrowser.open(FBOAuth.FACEBOOK_GRAPH_URL+'/oauth/authorize?'+urllib.urlencode(
                                {'client_id':FBOAuth.CLIENT_ID,
                                 'redirect_uri':FBOAuth.REDIRECT_URI,
                                 'scope':'read_stream, publish_stream'}))
        finally:
            os.dup2(savout, 1)

        FBOAuth.SECRET_CODE = raw_input("Secret Code: ")
        self.save_secret_code(FBOAuth.SECRET_CODE)

        return FBOAuth.SECRET_CODE

    def access_token(self):

        if not FBOAuth.SECRET_CODE:
            FBOAuth.SECRET_CODE = self.authorize()

        args = {'redirect_uri': FBOAuth.REDIRECT_URI,
                'client_id' : FBOAuth.CLIENT_ID,
                'client_secret':FBOAuth.CLIENT_SECRET,
                'code':FBOAuth.SECRET_CODE,}
        
        access_token = urllib.urlopen(FBOAuth.FACEBOOK_GRAPH_URL + "/oauth/access_token?" + urllib.urlencode(args)).read()
        access_token = urlparse.parse_qs(access_token)
        FBOAuth.ACCESS_TOKEN = access_token['access_token'][0]
        self.save_access_token(FBOAuth.ACCESS_TOKEN)
        return FBOAuth.ACCESS_TOKEN

    def get_graph_api(self):

        if not FBOAuth.ACCESS_TOKEN:
            self.access_token()

        return facebook.GraphAPI(FBOAuth.ACCESS_TOKEN)

    def invalidate_login(self):
        self.save_access_token(None)
        self.save_secret_code(None)
        exit(0)
    
    # The following four methods are using database.py for making SECRET_CODE and ACCESS_TOKEN persistent
    #----------------------------------------------------------------------------------------------------
    def get_secret_code(self):
        return self.database.get('SECRET_CODE') or None

    def save_secret_code(self, secret_code):
        self.database.save('SECRET_CODE', secret_code)

    def get_access_token(self):
        return self.database.get('ACCESS_TOKEN') or None

    def save_access_token(self, access_token):
        self.database.save('ACCESS_TOKEN', access_token)
    #-----------------------------------------------------------------------------------------------------

Let’s understand it. This class is responsible for handling the OAuth phases of authentications for FB, and is able to create and return an instance of

facebook.GraphApi()

ready to be used, because it’s authenticated by the user. The class variables just hold the API keys, the URI that Facebook needs to know for displaying the secret code to the user, the secret code inputted by the user after the permissions for the applications are gathered, and the final access token.

The constructor

__init__()

tries to get already stored secret codes and access tokens from my tiny database. I am omitting the details on how I implemented the tiny database but you can see this by looking at the source code of FacebookGreeter.

The following is a picture summarizing Facebook OAuth 2 authentication steps. It is taken from the developers page.

Facebook OAuth 2.0 phases

The

authorize()

method is responsible for getting User authorization to use the application and for gathering the secret code for getting access then. It opens a web browser and accesses

/oauth/authorize

, passing as parameters how to identify the application, the permissions requested to the user, and which callback URI must be called by Facebook in order to receive the secret code that the user must input. The lines of code related to

os

are needed in order to redirect standard output because of this issue. The secret code is then returned by this method.

The

access_token()

method is for getting the final access token from Facebook. As you note, it calls the previous method if the secret code has not been gathered yet. A call to

/oauth/access_token

is performed, including the same information plus the user provided secret code. Facebook will then return the so much desired access token and its expiration. In this tiny program I do not take care of the expiration because it was not needed for me. You should do.

Finally, the

get_grah_api()

method checks if an access token is present, otherwise it calls the previous method. You can notice the beauty of this reaction chain that ensures the present of the required data at each entry point. It returns an instance of

facebook.GraphApi()

that is authorized by the access token. This object is then used to query Facebook Graph Api (e.g.,

graph_api.get_object('/me/friends')['data']

.

I hope that this post clarifies how to login/authorize a Python Facebook application using OAuth 2.0 protocol.

About the author

dgraziotin

Dr. Daniel Graziotin received his PhD in computer science, software engineering at the Free University of Bozen-Bolzano, Italy. His research interests include human aspects in empirical software engineering with psychological measurements, Web engineering, and open science. He researches, publishes, and reviews for venues in software engineering, human-computer interaction, and psychology. Daniel is the founder of the psychoempirical software engineering discipline and guidelines. He is associate editor at the Journal of Open Research Software, academic editor at the Research Ideas and Outcomes (RIO) journal, and academic editor at the Open Communications in Computer Science journal. He is the local coordinator of the Italian Open science local group for the Open Knowledge Foundation. He is a member of ACM, SIGSOFT, and IEEE.

2 comments

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Ramblings of Daniel Graziotin

About Author

dgraziotin

Dr. Daniel Graziotin received his PhD in computer science, software engineering at the Free University of Bozen-Bolzano, Italy. His research interests include human aspects in empirical software engineering with psychological measurements, Web engineering, and open science. He researches, publishes, and reviews for venues in software engineering, human-computer interaction, and psychology. Daniel is the founder of the psychoempirical software engineering discipline and guidelines. He is associate editor at the Journal of Open Research Software, academic editor at the Research Ideas and Outcomes (RIO) journal, and academic editor at the Open Communications in Computer Science journal. He is the local coordinator of the Italian Open science local group for the Open Knowledge Foundation. He is a member of ACM, SIGSOFT, and IEEE.