Facebook oauth and Graph API with Django

August 23, 2010

In this post, I am going to describe how I integrated Facebook into Gourmious, so users could post their favorite dishes on Gourmious and on Facebook at the same time.

I wanted to keep the Gourmious login so the users could decide to login using their Gourmious credentials or using the Facebook login feature. I also wanted all users to have a Gourmious account so I didn’t allow users to login using their Facebook credentials if they didn’t have a Gourmious account. I decided to support the following 3 scenarios :

- user has a Django account and he logs in with Facebook. I associate his Facebook account to his Django account. This needs to be done only once.
- user does not have a Django account and tries to login using Facebook. I ask him first to create a Django account and I associate both accounts.
- user logs in using his Django credentials.

Facebook oauth is easier than the old Facebook connect. I was so happy to migrate to this new scheme.

I assume that you already have a server running your Django application and a Facebook app.

We are going to use the Django app django_facebook_oauth to make our life easier. Make sure you have python simplejson package installed.

Facebook Platform uses the OAuth 2.0 protocol for authentication and authorization. Read this first before continuing.
Facebook authentication

Clone the Django Facebook oauth app source code:

git clone http://github.com/dickeytk/django_facebook_oauth.git

Copy the folder django_facebook_oauth to your Django project apps folder and rename it to facebook.

We will assume that your apps folder is called apps.

Django settings.py

We need to make the following changes:

  • Add ‘apps.facebook.backend.FacebookBackend’ to AUTHENTICATION_BACKENDS.
  • Add ‘apps.facebook’ to INSTALLED_APPS
  • Add your Facebook API app ID: API_ID = xxxx.
  • Add your Facebook secret key: APP_SECRET = xxxx.

Django urls.py

Add the following to urlpatterns:

(r'', include('apps.facebook.urls')),

Database changes

You need to add the following table so you can associate Django user IDs with Facebook user IDs and store the Facebook session token. You can use the syncdb feature or create the table manually.

CREATE TABLE `facebook_facebookuser` (
  `id` int(11) NOT NULL auto_increment,
  `user_id` int(11) NOT NULL,
  `facebook_id` varchar(150) NOT NULL,
  `access_token` varchar(150) default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `user_id` (`user_id`),
  UNIQUE KEY `facebook_id` (`facebook_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Facebook authentication flow

1- user clicks on the Facebook login button
2- Django Facebook app authenticate view is called
3- facebook open graph authorize url is called with application ID, redirect uri = /authenticate, scope = ‘publish_stream’ to ask for permission to post to user’s wall
4- Dajgno Facebook app authenticate view is called with a parameter called code.
5- authentication using the Django Facebook app backend by passing the code back to Facebook along with the app secret.
6- Facebook returns a session token to be used for actions like posting messages to the user’s wall.
7- if the user is already in facebook_facebookuser table, login and redirect to home page. If user is not in the table, ask him to login using your app credentials so you can associate his Django app user id with his Facebook user id.

Django Facebook app views.py

I modified views.py to use my login form to associate Django user id with Facebook ID instead of using the register form included in the Django Facebook app.

When the token is returned and the user does not exist in the facebook table, I add a parameter to the request session to indicate that the user needs to login first so I can join the accounts.

if user != None:
  login(request, user)
  return HttpResponseRedirect('/')
else:
  # lines added
  request.session['ask_to_login_facebook'] = '1'
  return HttpResponseRedirect('/')
  #return HttpResponseRedirect('/register/')

I check for this session parameter in my template to popup a login form.

When the user enters his Django app credentials, I add an entry to the Facebook table before logging in the user. This operation just needs to be done once. After that, I know how to match a user Facebook id to the Django app user id so the user can directly login using the Facebook connect button.

This the code in my Django app views.py

# check credentials
user = authenticate(username=username, password=password)
if user is not None:
  if user.is_active:
      if 'fb_id' in request.session:
        fb_user = FacebookUser(user = user,
          facebook_id = request.session['fb_id'],
          access_token = request.session['fb_token'])
        fb_user.save()
        del request.session['fb_id']
        del request.session['fb_token']
      login(request, user)
      status = 0
    else:
      status = 1
  else:
    status = 1
    msg = _('Invalid password')

Django Facebook app backend.py

I modified this file to also add the token to the request session so I can use it when I add an entry to the facebook user table in views.py.

try:
  fb_user = FacebookUser.objects.get(facebook_id = str(profile["id"]))
except FacebookUser.DoesNotExist:
  request.session['fb_id'] = profile['id']
  # line added
  request.session['fb_token'] = access_token
  return None
fb_user.access_token = access_token
fb_user.save()

Django app template

This is what I added to my Django app login form to support the Facebook login button.

<form id="form_authenticate" action="/authenticate" method="POST">
  <p>blablabla</a></p>
  <input id="form_authenticate_button" type="image" src="link_to_facebook_button"                    onClick="javascript:$('#form_authenticate').submit();">
</form>

I also added a ‘Post to Facebook’ check box to my ‘Post dish review’ form so the user can decide what gets posted to his Facebook wall.

Post a message on the user’s wall

I used to post messages using the Javascript SDK but I now do it on the server side using the Facebook python SDK.

Add the file facebook.py (http://github.com/facebook/python-sdk/blob/master/src/facebook.py) to the Facebook app folder and rename it facebook_sdk.py .

We call put_wall_post() to post a message to the user’s wall.

Here is the code I am using in my Django app views.py to format the parameters before calling put_wall_post() .

def post_to_facebook(request):
  try:
    fb_user = FacebookUser.objects.get(user = request.user)
    # GraphAPI is the main class from facebook_sdp.py
    graph = GraphAPI(fb_user.access_token)
    attachment = {}
    message = 'test message'
    caption = 'test caption'
    attachment['caption'] = caption
    attachment['name'] = 'test name'
    attachment['link'] = 'link_to_picture'
    attachment['description'] = 'test description'
    graph.put_wall_post(message, attachment)
    return 0
  except:
    logging.debug('Facebook post failed')

That’s it for now.

If you enjoyed this article, check out my web app Gourmious to discover and share your favorite restaurant dishes. It would be cool if you could add some of your favorite restaurant dishes.

tags: ,
posted in Uncategorized by Laurent Luce

Follow comments via the RSS Feed | Leave a comment | Trackback URL

10 Comments to "Facebook oauth and Graph API with Django"

  1. Eugene wrote:

    Hi,
    I have a couple of questions:

    1) Does the http://github.com/dickeytk/django_facebook_oauth.git refer to http://github.com/digvan/django_facebook_oauth or http://github.com/vikalp/django-facebook-oauth?

    2) Does the fb_token found in # check credentials refer to the oauth access token ?

    That’s all for now!.

  2. laurent wrote:

    1) it refers to the package: http://github.com/dickeytk/django_facebook_oauth

    2) Yes.

  3. patrick wrote:

    I’m new to Django and could use some debugging pointers. Should I be able to get this to work if my server is localhost?

    I’m seeing the following error:
    http://localhost:80/authenticate/({
    “error”: {
    “type”: “OAuthException”,
    “message”: “Missing redirect_uri parameter.”
    }
    });

    Thanks

  4. laurent wrote:

    @patrick

    It should work.

    What is redirect_uri set to in authenticate_view() in the Facebook Django library ? What is your Facebook application URL set to ?

  5. Miguel wrote:

    Hi, I have to do what you did with Gourmious, but I’m not sure about something, every time you log into the web is going to ask me to connect to facebook?, or just the first time and then asueme that I publish on facebook too?

  6. Laurent Luce wrote:

    @Miguel: The first step is to associate the facebook account to your web site account. I took this approach as I wanted every user to have an account on my side. You will have to look at the advantages and disadvantages of that versus only using their Facebook account. When this is done, the user just needs to click on the ‘Connect with Facebook’ button each time he wants to login using his facebook account. An access token will be received by the Django backend. You can use this access token to post on their wall.

  7. codepoet wrote:

    HI Laurent,

    The First code you have written ,for login, is in facebook app’s view or you have created your own view.

  8. Laurent Luce wrote:

    @codepoet The first code snippet is part of the function authenticate_view of the Facebook app view.

  9. codepoet wrote:

    Hi Laurent

    Thanks for your response. As per the details you have given i have cloned the application and put it in my project and rename it to facebook. when i check this facebook’s view i only found two methods ‘login’ and ‘callback’. The ‘authenticate_view’ is a new function you have writen?

  10. Rory wrote:

    Just as a hint. If you’re using pip & virtualenv (which you should use), you can just “pip install git+git:…..”. It would be easier than “git clone and then copy etc.”

Leave Your Comment

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org