B
L
O
G

Facebook Authentication Using PyQt Tools

Aug 22 2011

QtFacebook I've been messing around with Facebook Graph API lately. The motivation is to get Choqok working with Facebook. Then, I figured it wasn't THAT easy to add this functionality. Choqok uses QtOAuth, to keep it nice, I should add the functionality in QtOAuth, which only support OAuth 1.0. Since I am new to Qt programming, it will take sometime before I know how to do it properly. Well, it didn't prevent me from playing around the Facebook Graph API using Python. The following tutorial is for whoever wants to know the basic of Facebook Graph API and get your desktop app authenticated on facebook. Since I am using KDE4, I will use PyQt's Webkit, as all the necessary libraries are already installed. If you are using Gnome, try PyWebKitGtk.

There is a well written tutorial about Facebook's implementation of OAuth2.0 - Under the covers of OAuth 2.0 at Facebook. Some details on that post are outdated, but the principles of Facebook Graph API are still the same: QAuth2 uses SSL, get the access token and use it when you need to manipulate an user's account, end of the story.

Because of the SSL policy, it's understandable that Facebook didn't write any API for C++, as everything could be done in a web browser. There is no "official" python API anymore, although Facebook still has the project on github. Somehow, you can't install it via pip or easy-install. An example here uses the package to post message on your wall, if you want to follow the tutorial, I recommend you to install the sdk first. I also encourage you to read the source code of python-sdk, as it is clear and easy to read, nothing complex, just sending post requests to facebook and process json responses.

Getting Your App ID

First thing first, you need to create an app on facebook.com, regardless what type of apps you are going to make. Follow the instructions on facebook developer page, and setup an test app. You will need the App ID after creating an new app.

Let's Get Access Token!

What we are going to do is exactly what recommends by Facebook Document:

Our OAuth 2.0 implementation does not include explicit desktop app support. However, if your desktop app can embed a web browser (most desktop frameworks such as .NET, AIR and Cocoa support embedding browsers), you can use the client-side flow with one modification: a specific redirect_uri. Rather than requiring desktop apps to host a web server and populate the Site URL in the Developer App, we provide a specific URL you can use with desktop apps https://www.facebook.com/connect/login_success.html.

The code example listed below will try to get your test app authenticated and store the access token in a hidden file (~/.facebook_access_token). One thing to keep in mind is, the code is only for illustration purpose, don't use it in any place serious. The access token should not be handled like that; the example doesn't catch any error returned by facebook. There should be better ways to get the returned URL - I spent less than 5 minites to read QtWebkit doc and come up the get_access_token function. All I wanted was to get the job done. If you know better ways, please post them as comments.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# facebook_auth.py

import sys 
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *

# place to store our access token - in your home directory
TOKEN_FILE = os.path.expanduser('~/.facebook_access_token')

def store_token(access_token):
    try:
        f = open(TOKEN_FILE, 'w')
        f.write(access_token)
        f.close()
        print "Access token stored in " + TOKEN_FILE
    except IOError as err:
        print >> sys.stderr, "File can't be opened: %s " % err 
    except:
        print >> sys.stderr, "Access token can't be stored."

def get_access_token(url):
    """ 
    As documented in facebook document, you can use
    https://www.facebook.com/connect/login_success.html
    as redirect uri if you are developing a desktop app.
    """
    path = url.path()
    if path.endsWith(u'login_success.html'):
        """ 
        you can parse the url directly by using regular expression.
        But I will just use existing functions to do the job. 
        """
        url_string = url.toString()
        print url_string
        new_url = QUrl(url_string.replace('html#', 'html?'))
        print new_url
        access_token = new_url.queryItemValue('access_token')
        print access_token
        store_token(access_token)

def auth():
    # Replace the value of APP_ID with the one 
    # from your Facebook application's settings
    APP_ID = 'XXXXXXXXXXXXX'
    AUTH_URI = 'https://www.facebook.com/dialog/oauth'
    REDIRECT_URI = 'https://www.facebook.com/connect/login_success.html'
        
    """ 
    Permissions you ask a facebook user granting to your app.
    'offline_access' is needed if you don't want the 
    access token to expire.
    """
    EXTENDED_PERMS = [
        'user_about_me',
        'friends_about_me',
        'user_status',
        'friends_status',
        'user_relationships',
        'user_online_presence',
        'user_photos',
        'user_videos',
        'read_friendlists',
        'read_stream',
        'publish_stream',
        'user_groups',
        'offline_access',
    ]

    # Qt App
    app = QApplication(sys.argv)
    web = QWebView()

    # Construct our URL
    url = QUrl(AUTH_URI)
    url.addQueryItem('client_id', APP_ID)
    url.addQueryItem('redirect_uri', REDIRECT_URI)
    url.addQueryItem('scope', ','.join(EXTENDED_PERMS))
    url.addQueryItem('response_type', 'token')
    web.load(url)

    web.urlChanged.connect(get_access_token)
    web.show()
    sys.exit(app.exec_())

def read_token():
    try:
        access_token = open(TOKEN_FILE).read()
    except IOError:
        auth()
        access_token = open(TOKEN_FILE).read()
    except:
        access_token = None

    return access_token

if __name__ == "__main__":
    auth()

Save the file and make it executable. If you run the script, a simple browser will open and ask you to input Facebook username and password, after it will ask you to grant permissions to the app (check the screenshot below).

facebook permissions

By clicking 'Allow', you would see a simple white page with the word success on it, now, close the window. Normally, you should have .facebook_access_token in your home directory, unless something went wrong. Now that we have the access token, let's post a personal status update on your wall. Save the following code in another file, let's called it facebook_post.py.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# facebook_post.py

import sys 
import facebook
from facebook_auth import read_token

ACCESS_TOKEN = read_token()

try:
    msg = sys.argv[1]
except IndexError:
    print >> sys.stderr, \
            "Usage: facebook_auth msg"
    sys.exit()

gapi = facebook.GraphAPI(ACCESS_TOKEN)
wall_post = gapi.put_wall_post(msg)
print wall_post

Make it executable and try it out:

$ chmod a+x facebook_post.py
$ ./facebook_post.py "Post whatever you want, don't forget the quotation marks."

The script will post a message on your wall and return the ID of the message. Every Facebook object has an ID. You can access the content of the object by using this URI on a web browser:

https://graph.facebook.com/TRY_YOUR_MSG_ID_HERE?access_token=XXXXXXXXX

Check the Face book Graph API reference page if you want to know more arguments. 

Well, I hope you are having fun reading the tutorial. I am going back to the boring OAuth2.0 spec. See you next time!

Comments ( 0 )  |  hide comments  |  leave a comment