Kevin Witaker

A Random Collection of Internet Stuff

Adding Facebook authentication to your Django Application

As the internet becomes more interconnected, data of all kinds is more readily passing between different domains and applications. As a result, several centralized data “hubs” are cropping up that allow web users to enter data in once, and then allow applications to access it from there. This is in contrast to the age-old practice of requiring users to create unique accounts, and enter the same information for every single web site or application they wanted to use.

Unique user authentication systems are dead, at least for most of us.

As the Oatmeal so eloquently put it (hint: scroll down to #4), there’s really no reason for the vast majority of web applications to have their own “vaults” of user data, forcing the user to jump through hoops every time they go to a new site. Rather, it’s better now to use the growing number of public APIs to let users log into our applications with the accounts they already have. As far as a mostly public repository of user data goes, it’s hard to get better than Facebook.

The plan.

I’ve been toying around with a new web app as of late, and I decided from the outset that I would allow users to log into my system with their Facebook accounts. Eventually, I’ll add OpenID as well. The goal for me is to ask users to jump through a unique registration system only if they can’t log in with a pre-existing account from somewhere else.

With that in mind, let’s get started.

My setup.

Here is some information about my setup:

  • Ubuntu 10.4
  • PostgreSQL 8.4
  • Python 2.6
  • Django 1.2
  • Nginx
  • FastCGI for serving up my Django app.

If you are running things differently, you might have to make some minor modifications to get things working.

The plan.

The goal of this tutorial is to inject your Django app with some Facebook love. We will be setting up and installing the following:

Assumptions and prerequisites.

I’m assuming you’ve already gone through the trouble of setting up your stack and have a Django project started. If not, my friend Brandon Konkle has an excellent tutorial that will get you up and running.

Install memcached.

django-facebookconnect requires caching, so the first thing we will do is set up and install memcached. If you’d rather use database caching, or something else, you should read up on other options for caching in Django.

sudo apt-get install memcached

Then, open your project’s settings.py file and add the following line:

CACHE_BACKEND = 'memcached://127.0.0.1:11211/'

This tells Django to access a memcached server at port 11211 of the local host.

Install PyFacebook.

The django-facebookconnect app requires PyFacebook to be installed, and that makes sense, given that you’ll need the goodies in the package to make your app talk to Facebook.

I find it easiest to create a “code” directory in my home directory, and then pull things into it. I’m using git here to pull the files down, but you can use whatever you need to.

mkdir code
cd code
git clone http://github.com/sciyoshi/pyfacebook.git
cd pyfacebook
sudo python setup.py install

Note: It’s important to note that Ubuntu, for some reason, places all of your python packages in /usr/local/lib/python2.6/dist-packages, as opposed to the conventional site-packages directory. You’ll need to remember this if you want to remove PyFacebook or django-facebookconnect at a later date.

Install django-facebookconnect.

Head back to your code directory, and go grab the django-facebookconnect package. You’ll have to unzip it with your decompressor of choice.

cd ~/username/code
wget http://django-facebookconnect.googlecode.com/files/django-facebookconnect-0.1.zip
unzip django-facebookconnect-0.1.zip
cd django-facebookconnect-0.1

Once it’s unzipped, you’ll want to copy the “facebook” directory into your dist-packages directory.

sudo cp facebook /usr/local/lib/python2.6/dist-packages/

Configuring your application.

Let’s start by adding the facebook app to your installed apps. You’ll also need to add django-auth, if you don’t already have it. Move into your project directory and edit your settings.py file.

INSTALLED_APPS = (
    #---
    'django.contrib.auth',
    'django.contrib.sessions',
    'facebookconnect',
)

Next we need to add the middleware definitions in. Middleware can be very picky, so the order here is very important.

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'facebook.djangofb.FacebookMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'facebookconnect.middleware.FacebookConnectMiddleware',
)

Note: You may have some other middleware that isn’t included in the previous list. Just try to set the order as best you can; you might need to play with it to get everything working.

Now it’s time to define the authenticate backends. If you don’t have an AUTHENTICATION_BACKENDS group in your settings file, just add one.

AUTHENTICATION_BACKENDS = (
    'facebookconnect.models.FacebookBackend',
    'django.contrib.auth.backends.ModelBackend',
)

The last step to configuring your app is getting the Facebook-specific information in. This will require you to get a Facebook API key. Head over to their developer site and create an application. You’ll need to fill in the “Connect” information for your app once you get it set up as well.

With Facebook info in-hand, we can finish the settings.py file.

DUMMY_FACEBOOK_INFO = {
    'uid':0,
    'name':'(Private)',
    'first_name':'(Private)',
    'pic_square_with_logo':'http://www.facebook.com/pics/t_silhouette.gif',
    'affiliations':None,
    'status':None,
    'proxied_email':None,
}

FACEBOOK_API_KEY = '00000000000000000000000000000000'
FACEBOOK_SECRET_KEY = '00000000000000000000000000000000'
FACEBOOK_INTERNAL = True
#Cache facebook info for x seconds. Default is 30 minutes
FACEBOOK_CACHE_TIMEOUT = 1800

What we’ve done here is set up some dummy information for Facebook to display, in case something goes wrong with the connection. This happens from time to time, so Facebook attempts to break gracefully, rather than taking your site down with an ugly error message.

Underneath that is where you’ll put your API keys, and set the cache timeout limit. I don’t know what the FACEBOOK_INTERNAL call does, except that PyFacebook requires it. I’ve messaged the author of PyFacebook for some more information, and will update this post when I get it.

Configure URLs, and add the Facebook Connect button to your template.

Ok, we are almost ready to rock and roll. All that’s left is adding the new URLs, and putting the relevant tags into your templates. Open up your project’s urls.py file and make the following changes.

urlpatterns = patterns('',
    ...,
    (r'^facebook/', include('facebookconnect.urls')),
)

Now, open up your registration/login.html template, and add these tags.

#Top of the page
{% load facebook_tags %}

#In the head tag
{% facebook_js %}
{% initialize_facebook_connect %}

#Wherever you want the button to appear
{% show_connect_button %}

Start your server up, and voila, you should have a Connect with Facebook button on your login page. Clicking on it should take you to a Facebook Login page, and when you successfully log in, you will be redirected to the accounts/profile page. Pretty cool, eh?

But wait, there’s more!

There are some very interesting options available to the django-facebookconnect app, and I suggest you take a look at the installation instructions to learn more. Changing the page that Facebook redirects to after login might be particularly useful.

Letting your users utilize a pre-existing account, especially one that is most likely central to their internet lives, will encourage casual browsers to become actual users. Adding additional options, such as OpenID, is also advisable so you can capitalize on the wealth of data that’s already out there. Not having to worry about account management also frees up time to for you to streamline your application.

Running Django with Postgres, Nginx, and FastCGI on Ubuntu 10.4

Last week, I decided that I needed to virtualize my Django test environment, and get it off my MacBook. I plan on upgrading at some point in the future, so not having to go through all the steps to set up the environment again would be nice. Plus, getting Django running on the MacBook was a bit complicated, and I’d like to avoid that. So, with the goal of “easy installation” in mind, I started working on a new VM.

*Update*:

A friend and co-worker, Brandon Konkle, just posted a great article using the basics I listed below, but adding Green Unicorn instead of FastCGI, and with virtualenv to manage dependencies. Great read.

The Stack

Starting off, I decided on a few main goals for my VM:

  1. It would run on Ubuntu, because that’s the Linux distro I’m most familiar with.
  2. The web server would need to have a low memory footprint.
  3. It shouldn’t run MySQL, as some of the Django muckity-mucks recommend against it.
So, the stack ended up being:
  • Ubuntu Server 10.4 x64
  • Nginx
  • PostgreSQL 8
  • Django 1.2

Now, some of this stuff (Ubuntu and Django) are versions that have very recently been released. If you aren’t comfortable running the latest revisions (or if your app has specific needs for older versions), pick those instead.

The Problem

Unfortunately, I couldn’t find a set of instructions detailing exactly what I want. Plenty of good posts on running Django on Apache, or using MySQL, but nothing for the stack I had decided on. So, I culled what I could from various posts, and decided to aggregate the steps here.

Assumptions

This post assumes that you have some knowledge of how to get Ubuntu up and running. If you need additional information for that, I recommend checking out the Slice Host Articles. They’re a top-notch resource for all kinds of info.

Note: If you’re on a MacBook, you’ll need to select the “Apple Laptop” keyboard during Ubuntu setup. If you don’t do this, your keys may act funky. To reconfigure, you can run:

sudo dpkg-reconfigure console-setup

Install Nginx

Once you’ve got Ubuntu up and running, it’s time to get Nginx going. I’m just going to link to the slicehost articles for this, because, frankly, they’ve already gone through the trouble on this part.

  1. Installing Nginx with Aptitude
  2. Basic Nginx configuration
  3. Nginx virtual host setup

I don’t do the last step, because I’m building this on a VM, so instead I just use Nginx’s default site configuration file. You should use whichever option is better for your environment.

Install PostgreSQL

Luckily, Ubuntu makes it pretty easy to get PostgreSQL installed.

sudo apt-get install postgresql pgadmin3 python-psycopg2

This will install PosgresSQL, PG Admin III, and PsycoPG (which you need for Python to talk to the database).

You’ll also want to setup a database user account.

sudo su -
passwd postgres
su postgres
psql template1

The last command will open a PosgreSQL command line, where you can type the following, making the necessary substitutions:

ALTER USER postgres WITH ENCRYPTED PASSWORD 'mypassword';

Then exit PSQL.

Install Django

To install Django, it’s easiest to install subversion first, and then clone the repository.

sudo apt-get install subversion
svn co http://code.djangoproject.com/svn/django/trunk django_trunk
sudo python django_trunk/setup.py install

Once Django is installed, you can verify it by opening up your Python console:

python
import django
print django.VERSION
import psycopg2
psycopg2.apilevel
exit()

Install flup

We also need to install flup, so that FastCGI can pass the Django requests from Nginx to the Django server.

sudo wget http://www.saddi.com/software/flup/dist/flup-1.0.2-py2.5.egg
sudo easy_install flup-1.0.2-py2.5.egg

Build Your Test Project

Now that everything is installed, it’s time to make sure it worked. Build out an empty test project.

cd ~/public_html/testproject/private
django-admin.py startproject testproject
cd testproject
python manage.py startapp myapp
nano settings.py

You’ll then need to enter some database information.

DATABASE_ENGINE = 'django.db.backends.postgresql_psycopg2'
DATABASE_NAME = 'testproject'
DATABASE_USER = 'postgres'
DATABASE_PASSWORD = 'mypasswd'
DATABASE_HOST = ''
DATABASE_PORT = ''            

Don’t worry about setting up the media directories and such, as we just want the default “Welcome to Django” page for the time being.

Nginx Configuration

Now that the backend stuff is good to go, it’s time to get your Nginx configuration taken care of. If you installed Nginx via aptitude, you’ll want to stop the server.

sudo /etc/init.d/nginx stop

Since I’m just using this as a test bed, accessed locally, I made my changes in Nginx’s default site configuration. Feel free to add another virtual host if you need to.

sudo nano /etc/nginx/sites-available/default

Once you’re in the configuration file, change it to this:

server
{
    listen   80;

    server_name localhost;
    access_log /home/username/public_html/testproject/log/localhost.access.log;
    error_log /home/username/public_html/testproject/log/localhost.error.log;
    root /home/username/public_html/testproject/public;

    location /site_media
    {
        root /home/username/public_html/testproject/public;
    }

    location /
    {
        # host and port to fastcgi server
        fastcgi_pass 127.0.0.1:8081;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param REQUEST_METHOD $request_method;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param CONTENT_TYPE $content_type;
        fastcgi_param CONTENT_LENGTH $content_length;
        fastcgi_pass_header Authorization;
        fastcgi_intercept_errors off;
    }
}

This will ensure that all requests for dynamic content (read: Django) get passed to Django, and all static content (images, stylesheets, scripts, etc.) just go through Nginx as normal. You don’t have to use port 8081 here, but take note of whichever port you do decide on, as we’ll need it next.

Run Django as a FastCGI process

The last thing we need to do is get Django set up to run as a FastCGI process.

cd ~/public_html/testproject/
python manage.py runfcgi host=127.0.0.1 port=8081 --settings=settings

Also, don’t forget to restart Nginx.

sudo /etc/init.d/nginx start

The End

And that’s it! You should now be able to head over to the VM’s IP address, and get the “Welcome to Django - Get Working!” page. If not, you should review the steps I’ve placed here, and see if you missed anything. There’s also the possibility that I missed something, in which case, feel free to flog me in the comments below.

Gotta Give Respect

Respect needs to be paid to David McLaughlin and Antonio Cangiano, both of whom produced a lot of the information that I’ve repurposed here. You should read their guides if you have any additional questions (David goes into FTP servers and getting your Admin media synced, for instance).