Ghetto Facebook Registration with Django

Monday, October 8, 2012

Tags: apple, programming, python, django

I’m going to quickly walk you through how to build a server-side Facebook registration flow with Django. This is really basic and doesn’t rely on special libraries aside from httplib2 and urlib which are pretty standard.

First you need to create an app. I set my App Domain to localhost and Site URL to http://localhost:8000 for development purposes. You’ll probably need to do the same if you’re using Django’s built in development server. Copy over your App ID and App Secret into your settings.py file:

FACEBOOK_APP_ID = 'YOUR_FACEBOOK_APP_ID'
FACEBOOK_SECRET_KEY = 'YOUR_FACEBOOK_APP_SECRET'

Now lets add a login button to your site, you can put this anywhere:

<a class="ui-button" href="https://www.facebook.com/dialog/oauth?
  client_id=YOUR_FACEBOOK_APP_ID
  &amp;redirect_uri=http://localhost:8000/facebook/">Log In with Facebook</a>

Don’t forget to replace YOUR_FACEBOOK_APP_ID with your App ID. It’s okay if this is hardcoded. Just make sure you don’t accidentally expose your App Secret, this should not be used publicly.

You’ll notice we put http://localhost:8000/facebook/ as our redirect URI in the button above. Now we need to create a view to handle this request because Facebook will hand us a “code” at that location which is what we’ll need to retrieve an access token for the user, thus completing the process. Add the following to your urls.py:

url(r'^facebook/$', 'views.facebook'),

Now add the following to your views.py:

import httplib2
import urllib

from django.http import HttpResponseRedirect
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
from django.utils import simplejson as json

from profiles.models import Profile

def facebook(request):
  params = {
    'client_id': settings.FACEBOOK_APP_ID,
    'redirect_uri': 'http://localhost:8000/registration/facebook/',
    'client_secret': settings.FACEBOOK_SECRET_KEY,
    'code': request.GET['code']
  }

  http = httplib2.Http(timeout=15)
  response, content = http.request('https://graph.facebook.com/oauth/access_token?%s' % urllib.urlencode(params))

  # Find access token and expire (this is really gross)
  params = content.split('&')
  ACCESS_TOKEN = params[0].split('=')[1]
  EXPIRE = params[1].split('=')[1]

  # Get basic information about the person
  response, content = http.request('https://graph.facebook.com/me?access_token=%s' % ACCESS_TOKEN)
  data = json.loads(content)

  # Try to find existing profile, create a new user if one doesn't exist
  try:
    profile = Profile.objects.get(facebook_uid=data['id'])
  except Profile.DoesNotExist:
    user = User.objects.create_user(data['username'], data['email'], data['id'])
    profile = user.get_profile()
    profile.facebook_uid = data['id']

  # Update token and expire fields on profile
  profile.facebook_access_token = ACCESS_TOKEN
  profile.facebook_access_token_expires = EXPIRE
  profile.save()

  # Authenticate and log user in
  user = authenticate(username=profile.user.username, password=profile.facebook_uid)
  login(request, user)

  return HttpResponseRedirect('/')

One thing you’ll immediately notice is I’m importing a Profile model. All you need to do here is create a profiles app that has a single model with a foreign key to a user and some fields to store our access token and when that token expires:

from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
  user = models.ForeignKey(User, unique=True)

  facebook_uid = models.PositiveIntegerField(blank=True, null=True)
  facebook_access_token = models.CharField(blank=True, max_length=255)
  facebook_access_token_expires = models.PositiveIntegerField(blank=True, null=True)

And then add the following to your settings.py so you can use the “get_profile()” convenience method on user objects:

AUTH_PROFILE_MODULE = 'profiles.profile'

There you have it. A really hacky Facebook registration flow for Django.

Some will probably notice I didn’t use the word OAuth anywhere in this post. Every time I see that term my eyes gloss over and my buzzword bullshit detector flips on. OAuth is a very simple concept that’s often over explained—hopefully people can run through this tutorial and grasp what’s happening by just looking at the code.