Tutorial: Adding Facebook/Twitter/Google Authentication to a Django Application

Image via http://www.djangopony.com/

Image via http://www.djangopony.com/

I needed to add Facebook authentication to a Django app today, and instead of writing it directly against the Facebook API (or re-implementing the OAuth2 dance again), I decided to look around and see if there’s a pre-packaged solution for this common task. Turns out, there’s an excellent project called Python Social Auth, and it covers pretty much any social website with an authentication API.

As it often happens with amazing open-source projects, the documentation is somewhat minimalistic. One key piece that I could not find was a tutorial. This post is aiming to fill that gap. In this tutorial, we will use Facebook, Twitter and Google, as the most common social APIs, but we could easily substitute them with LinkedIn, Yahoo, Forsquare, or a bunch of other providers supported by Python Social Auth library.

If you are comfortable with Django, feel free to skip to Step2.

Step 0. Start a simple Django project

Let’s begin with a barebones Django project named “thirdauth”, named in honour of third-party authentication.

$ django-admin.py startproject thirdauth
$ tree thirdauth/
thirdauth/
├── manage.py
└── thirdauth
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Running ./manage.py syncdb and then ./manage.py runserver and navigating to localhost:8000 will show the familiar “It worked!” Django page. Let’s put some custom application code in place, so that we can tell whether the current user is authenticated or anonymous.

Step 1. Show current user’s authentication status

Let’s throw together a simple basic page, add CSS, JavaScript and fonts from Twitter Bootstrap, and add a view for home page. For this tutorial, we won’t need custom models or any other views.

NOTE: if you need help with Django views, templates and settings, please check out the Django tutorial.

Another NOTE: Bootstrap is Twitter’s basic set of CSS and JavaScript to help make even minimal web interfaces look and behave consistently well. You do not have to use it for this tutorial, it adds only aesthetic side – it adds a polished feel to things. Bootstrap can be downloaded from http://getbootstrap.com/

Now, the very small customizations we’ll add are:

  • Add ‘thirdauth’ to INSTALLED_APPS
  • Create the template for the home page
  • Add a view for the home page
  • Add a URL pointing to the home page view

Relevant portion of settings.py:

INSTALLED_APPS = (
  'django.contrib.admin',
  'django.contrib.auth',
  ...
  'thirdauth',
)

Template: thirdauth/base.html:

<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>{% block title %}Third-party Authentication Tutorial{% endblock %}</title>

   <!-- Bootstrap -->
   <link href="/static/css/bootstrap.min.css" rel="stylesheet">
   <link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
   <link href="/static/css/fbposter.css" rel="stylesheet">

   <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
   <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
   <!--[if lt IE 9]>
     <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
     <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
   <![endif]-->
 </head>
 <body>
   {% block main %}{% endblock %}
   <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
   <!-- Include all compiled plugins (below), or include individual files as needed -->
   <script src="/static/js/bootstrap.min.js"></script>
 </body>
</html>

Template: thirdauth/home.html:

{% extends 'thirdauth/base.html' %}

{% block main %}
 <div>
 <h1>Third-party authentication demo</h1>

 <p>
   {% if user and not user.is_anonymous %}
     Hello {{ user.get_full_name|default:user.username }}!
   {% else %}
     I don’t think we’ve met before.
   {% endif %}
 </p>
 </div>
{% endblock %}

File views.py:

from django.shortcuts import render_to_response
from django.template.context import RequestContext

def home(request):
   context = RequestContext(request,
                           {'user': request.user})
   return render_to_response('thirdauth/home.html',
                             context_instance=context)

File urls.py:

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
   url(r'^$', 'thirdauth.views.home', name='home'),
   url(r'^admin/', include(admin.site.urls)),
)

Now, when we refresh the page, we will see:

Fair enough – we have not authenticated yet. To make sure our identity-checking template works, try logging in to Django admin with the default Django authentication (assuming you created an admin account on Step 0). You should see a different message: “Hello admin!”, or something like that.

Step 2. Install Python Social Auth

First, let’s add it to our virtualenv:

pip install python-social-auth

Second, let’s make some modifications to our settings.py to include python-social-auth in our project:

INSTALLED_APPS = (
   ...
   'social.apps.django_app.default',
   ...
)

TEMPLATE_CONTEXT_PROCESSORS = (
   'django.contrib.auth.context_processors.auth',
   'django.core.context_processors.debug',
   'django.core.context_processors.i18n',
   'django.core.context_processors.media',
   'django.core.context_processors.static',
   'django.core.context_processors.tz',
   'django.contrib.messages.context_processors.messages',
   'social.apps.django_app.context_processors.backends',
   'social.apps.django_app.context_processors.login_redirect',
)

AUTHENTICATION_BACKENDS = (
   'social.backends.facebook.FacebookOAuth2',
   'social.backends.google.GoogleOAuth2',
   'social.backends.twitter.TwitterOAuth',
   'django.contrib.auth.backends.ModelBackend',
)

Let’s update the urls module to include the new group of URLs:

urlpatterns = patterns('',
...
url('', include('social.apps.django_app.urls', namespace='social')),
...
)

And finally, let’s update the database models:

./manage.py syncdb

Now, if we runserver again, and navigate to Django admin, we’ll see three new tables: Associations, Nonces, and User social auths. This last one will contain the records of our users’ social accounts when they use social networks to authenticate from our app.

We are almost there. Let’s add some links for logging in and logging out, and then we’ll start adding application IDs for social apps.

Step 3. Add links for logging in and logging out.

Since we’ll be logging in and out multiple times, let’s include django.contrib.auth URLs into our URLs configuration:

urlpatterns = patterns('',
   ...
   url('', include('django.contrib.auth.urls', namespace='auth')),
   ...
)

Let’s modify our Home page template like this:

{% extends 'thirdauth/base.html' %}

{% block main %}
 <div>
 <h1>Third-party authentication demo</h1>

 <p>
   <ul>
   {% if user and not user.is_anonymous %}
     <li>
       <a>Hello {{ user.get_full_name|default:user.username }}!</a>
     </li>
     <li>
       <a href="{% url 'auth:logout' %}?next={{ request.path }}">Logout</a>
     </li>
   {% else %}
     <li>
       <a href="{% url 'social:begin' 'facebook' %}?next={{ request.path }}">Login with Facebook</a>
     </li>
     <li>
       <a href="{% url 'social:begin' 'google-oauth2' %}?next={{ request.path }}">Login with Google</a>
     </li>
     <li>
       <a href="{% url 'social:begin' 'twitter' %}?next={{ request.path }}">Login with Twitter</a>
     </li>
   {% endif %}
   </ul>
 </p>
 </div>
{% endblock %}

For the login and logout links in this template to work correctly, we need to modify a few things. First, let’s take care of logout, it’s easier. Just add ‘request’ to the context object that we pass into template-rendering code. Updated views.py:

from django.shortcuts import render_to_response
from django.template.context import RequestContext

def home(request):
   context = RequestContext(request,
                           {'request': request,
                            'user': request.user})
   return render_to_response('thirdauth/home.html',
                             context_instance=context)

For login to work, let’s first add a LOGIN_REDIRECT_URL parameter to settings (to prevent the default /account/profile from raising a 404):

LOGIN_REDIRECT_URL = '/'

And then start adding API-specific parameters for social networks. Right this moment, if you click on any of the “login with X” links, you’ll get redirected to the corresponding social site, but will get an error about invalid client ID. That’s because we have not provided any client IDs yet.

Step 4. Get Client IDs for the social sites.

For all the social networks we are using in this demo, the process of obtaining an OAuth2 client ID (also known as application ID) is pretty similar. All of them will require that your application has a “real” URL – that is, not http://127.0.0.1 or http://localhost. You can add an entry in your /etc/hosts file that maps 127.0.0.1 to something like “test1.com”, and the URL of your application becomes http://test1.com:8000 – that is good enough for testing. You can change it in the social app settings when it goes into production.

Facebook

  • Go to https://developers.facebook.com/apps/?action=create and click the green “Create New App” button.
  • In the settings of the newly-created application, click “Add Platform”. From the options provided, choose Web, and fill in the URL of the site (http://test1.com:8000 in our example).
  • Copy the App ID and App Secret, and place them into settings.py file:
    SOCIAL_AUTH_FACEBOOK_KEY = …
    SOCIAL_AUTH_FACEBOOK_SECRET = …
  • This should be enough to get your app to login with Facebook! Try logging in and out – you should get redirected between your app and FB OAuth2 service, and a new record in the User social auths table will get created, along with a new User record pointing to it.

Google

  • Go to https://console.developers.google.com/ and create a new application.
  • Under APIs and Auth > Credentials, create a new Client ID.
  • Make sure to specify the right callback URL: http://test1.com:8000/complete/google-oauth2/
  • Copy the values into settings file:
    SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = …
    SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = …

Twitter

  • Go to https://apps.twitter.com/app/new and create the new application
  • The callback URL should be something like http://test1.com:8000/complete/twitter/
  • Copy the values into settings.py:
    SOCIAL_AUTH_TWITTER_KEY = …
    SOCIAL_AUTH_TWITTER_SECRET = …

What’s next?

Python Social Auth can provide more than just authentication. By customizing pipeline, we can get account information for our users, manage accounts through email confirmation, and more. But those topics are beyond the scope of this tutorial.

32 Comments

  1. Why are you using render_to_response and RequestContext when django.shortcuts.render does the same thing?

    Reply
    • Out of habit, I guess. I am glad Django keeps older methods around for people who got them ingrained.

      Reply
  2. a note: while its fine for this tutorial to put oauth keys in settings.py, you should never do this in practice (if your code is open source). Instead, environment variables should be used and they keys should be stored in an untracked file.

    Reply
    • Thanks burke, that’s a fair point. A tutorial may prefer clarity over security, but a note on best practices was needed.

      Reply
      • Hey, could you educate me on how to do that? I’m a beginner.
        Thanks :)

        Reply
  3. Thank you!

    Reply
  4. Thanks for the clear tutorial,
    I have used this in my app i got small problem,
    When i logging with facebook or google or twitter its fine and redirecting to my app ,when i click on logout its fine and logging out from my app and redirect to home page, but “Its not logging out from the facebook or google or twitter.

    How to do this, when i logging with social API and logging out in my app then the social API also should be logout.

    Reply
  5. Hey Vlad, Excellent work. This is a thorough tutorial for all python users.If you to explore more about python, you can visit our python course curriculum here http://www.fireboxtraining.com/python.

    Reply
  6. Hi, Vlad. This is a very useful tutorial. I hope you can still clarify one thing for me. I’m not sure what you exactly meant by this:

    “Make sure to specify the right callback URL: http://test1.com:8000/complete/google-oauth2/

    Where do I specify that callback?

    Reply
  7. I have followed all the steps and when I click on ‘Login with Google’ I am redirected to google sign in page once I login to google and grant access to my app by clicking ‘Accept’ on consent screen I am getting error as “HTTPError at /complete/google-oauth2/” – 403 Client Error: Forbidden.Please help me in solving this.

    Reply
  8. One point to make is to be sure you have made the google+ API setting AND filled out the Consent screen correctly before creating your Client ID, if you change those settings afterward you will need to regen the Client ID.

    Reply
  9. Thank you for this post.
    I follow this tutorial but I am having an issue. When I try to logging with facebook, it told me than one url pass is not valid. However I put my domain app and my site url. They are both good like my id and secret.
    My called url is https://www.facebook.com/dialog/oauth?state=5CNV248EbWPg3A0dtTmHWksmzNqhBwBP&redirect_uri=http%3A%2F%2F172.20.0.125%2Fcomplete%2Ffacebook%2F%3Fredirect_state%3D5CNV248EbWPg3A0dtTmHWksmzNqhBwBP&client_id=xxxxxxxxxx
    How can i resolved it?

    Reply
  10. nice tutorial, can i update this and put it in my blog site?

    Reply
    • Where can i find that fbposter.css

      Reply
    • I am getting “‘utf8′ codec can’t decode byte 0x92 in position 318: invalid start byte” error

      Reply
    • Successfully executed your sample project.It is done.But how can i get the other data like ’email, gender, first_name’ etc

      Reply
  11. The best tutorial EVER for Django and social auth !!!
    Thank you very much man !!!

    Reply
  12. this is probably the one tutorial that worked at first try… but you forgot to add template path in settings.py , if you don’t to that the template base.html and home.html is not found

    Reply
  13. Cool Would love to see the pipeline example to ask for an email. There is very little documentation or examples on how to do that with Django and Python Social.

    Reply
  14. Perfect Tutorial!! I’m also looking for some tutorial for the next step: get more data like profile image from the user connected with facebook.

    Reply
    • Two more steps needed for the app to find the templates:

      1. Create the tree under the project folder
      /templates/thirdauth

      Save the home.html and base.html here.

      2. Need to edit the settings.py :

      Add these lines:

      SETTINGS_PATH = os.path.dirname(__file__)
      PROJECT_PATH = os.path.join(SETTINGS_PATH, os.pardir)
      PROJECT_PATH = os.path.abspath(PROJECT_PATH)
      TEMPLATES_PATH = os.path.join(PROJECT_PATH, “templates”)

      TEMPLATE_DIRS = (
      TEMPLATES_PATH,
      )

      Reply
  15. Great tutorial! I am a Django novice and found this very helpful. But there are two very major flaws. Admin please update the post.
    The two flaws are :

    1. for the error: “templates not found” or “cannot locate home.html”
    reason: the template files base.html and home.html are placed incorrectly. In the tutorial the paths are ambiguously mentioned as “thirdauth/base.html” and “thirdauth/home.html”. Although it is a Django rule to place templates in the ‘templates/appname’ folder but stating it in a novice tutorial such as this is always beneficial.
    solution: place the templates base.html and home.html in “/templates/thirdauth/”

    2. for the error: “cannot import name is_secure_transport”
    reason: No real idea but probably the openAuthentication libraries are outdated. Googled and found the solution.
    solution: at the Linux terminal type… “sudo pip install oauthlib –upgrade”

    Reply
  16. Thanks for such a great project and nice tutorial! )

    Reply
  17. how can I save this data in another table?

    Reply
  18. I tried to do a ‘./manage.py syncdb’ and I got:

    django.core.exceptions.ImproperlyConfigured: ImportError social.apps.django_app.default: cannot import name force_text

    :(

    Reply
  19. Thanks! This tutorial helped to setup social login on my website.

    Reply
  20. Hi. I have followed your tutorial.

    I am having this error:

    no module named thirdauth.social.apps.django_app.

    I don’t know why..

    Reply
  21. Hey thanks for this! Great.

    Is this your site Artandlogic?

    Reply

Trackbacks/Pingbacks

  1. Python/Django Social Auth | DiRaOLinux - […] http://www.artandlogic.com/blog/2014/04/tutorial-adding-facebooktwittergoogle-authentication-to-a-dj… […]
  2. Art & Logic – » 2014 Review: Day 4 - […] Tutorial: Adding Facebook/Twitter/Google Authentication to a Django Application […]

Submit a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>