yeti logo icon
Close Icon
contact us
We'll reply within 24 hours.
Thank you! Your message has been received!
A yeti hand giving a thumb's up
Oops! Something went wrong while submitting the form.

Social Auth with Django REST Framework

By
-
June 24, 2015

This post is part 3 of a series on using OAuth with Django REST Framework. Part 2 focused on setting up your own OAuth2 server for your application, part 4 offers a richer explanation of server vs. client oauth flows, and part 5 is about integrating parts 2 & 3. For a summary of my thoughts on the process, see the series overview.

This post is to help you set up Django REST Framework with Python Social Auth; the goal here is to allow users to sign up and sign in to your app using Facebook, Twitter, etc., using API endpoints that you manage with Django REST Framework. If you are using Tastypie instead of DRF, see our complemetary piece: Integrating Django, Tastypie & Python Social Auth.

1. Install and Configure

I'm assuming that you already have a Django project set up using DRF and some standard variant on a Django User model. With that out of the way, you just need to do a simple pip install python-social-auth and follow the Django configuration instructions. Don't forget to migrate your database.

2. Request / Response

Your front-end application will pass up the following data in a request:

{    "provider": "facebook",  // or twitter, instagram, etc.    "access_token": "ABC123",  // user's access_token assigned by provider    "access_token_secret": "DEF456"  // required by some providers, e.g. Twitter}

You'll need to add a route to your view:

urlpatterns = patterns('',    url(r'^social_sign_up/$', views.SocialSignUp.as_view(), name="social_sign_up"),)

And the view above responds with your user instance (my serializer is just returning some basic user data).

3. SocialSignUp View

This is the bulk of the interesting work. Python social auth is a great out of the box solution using Django's views and templates, but takes a little picking apart to get to work with your DRF API. Python social auth knows how to use Django's views and urls to go through a server-side oauth flow. But if we want to initialize that process within our DRF view, we'll have to manually call some of the functions python social auth provides.

from django.contrib.auth import get_user_modelfrom rest_framework import statusfrom rest_framework.response import Responsefrom social.apps.django_app import load_strategyfrom social.apps.django_app.utils import load_backendfrom social.backends.oauth import BaseOAuth1, BaseOAuth2from social.exceptions import AuthAlreadyAssociatedUser = get_user_model()class SocialSignUp(generics.CreateAPIView):    queryset = User.objects.all()    serializer_class = SocialSignUpSerializer    # This permission is nothing special, see part 2 of this series to see its entirety    permission_classes = (IsAuthenticatedOrCreate,)    def create(self, request, *args, **kwargs):        """        Override `create` instead of `perform_create` to access request        request is necessary for `load_strategy`        """        serializer = self.get_serializer(data=request.data)        serializer.is_valid(raise_exception=True)        provider = request.data['provider']        # If this request was made with an authenticated user, try to associate this social         # account with it        authed_user = request.user if not request.user.is_anonymous() else None        # `strategy` is a python-social-auth concept referencing the Python framework to        # be used (Django, Flask, etc.). By passing `request` to `load_strategy`, PSA         # knows to use the Django strategy        strategy = load_strategy(request)        # Now we get the backend that corresponds to our user's social auth provider        # e.g., Facebook, Twitter, etc.        backend = load_backend(strategy=strategy, name=provider, redirect_uri=None)        if isinstance(backend, BaseOAuth1):            # Twitter, for example, uses OAuth1 and requires that you also pass            # an `oauth_token_secret` with your authentication request            token = {                'oauth_token': request.data['access_token'],                'oauth_token_secret': request.data['access_token_secret'],            }        elif isinstance(backend, BaseOAuth2):            # We're using oauth's implicit grant type (usually used for web and mobile             # applications), so all we have to pass here is an access_token            token = request.data['access_token']        try:            # if `authed_user` is None, python-social-auth will make a new user,            # else this social account will be associated with the user you pass in            user = backend.do_auth(token, user=authed_user)        except AuthAlreadyAssociated:            # You can't associate a social account with more than user            return Response({"errors": "That social media account is already in use"},                            status=status.HTTP_400_BAD_REQUEST)        if user and user.is_active:            # if the access token was set to an empty string, then save the access token             # from the request            auth_created = user.social_auth.get(provider=provider)            if not auth_created.extra_data['access_token']:                # Facebook for example will return the access_token in its response to you.                 # This access_token is then saved for your future use. However, others                 # e.g., Instagram do not respond with the access_token that you just                 # provided. We save it here so it can be used to make subsequent calls.                auth_created.extra_data['access_token'] = token                auth_created.save()            # Set instance since we are not calling `serializer.save()`            serializer.instance = user            headers = self.get_success_headers(serializer.data)            return Response(serializer.data, status=status.HTTP_201_CREATED,                             headers=headers)        else:            return Response({"errors": "Error with social authentication"},                            status=status.HTTP_400_BAD_REQUEST)

What's going on here? We're handling a few different cases:

Admittedly this view is a bit long and not the most OO way to handle the nuances of all the different social providers. Ideally you could write something similar to PSA's provider classes that you could plug in here. Currently this has been tested with Facebook, Twitter, and Instagram.

That's it! You should now be able to use your app to sign up or login users from their social accounts (both will use the same view).

Note

If you're reading this and wondering how to get this access token that you are passing to your API, the answer is that your front-end application is doing that. For example, you have a JavaScript or mobile application that is using the Facebook SDK, or is manually going through the provider's OAuth flow. The mechanics of that are outside the scope of this post, but to be clear, all that should be happening in the front-end. When you look at the documentation for whatever provider you are using, you will want to follow the instructions for the implicit or client-side oauth flow. Yes, DRF and python-social-auth are working server-side, but getting the access token is happening client-side, so that's the flow you want to follow. Essentially, the entire auth flow happens client-side--python social auth is just helping us request information from the social providers and save associations to the user. More detail on those processes in the next post in this series.

You Might also like...

Busting 5 App Development Myths

The following are five pervasive myths about why app development should be a simple process — and the real reasons that a good product takes time.

Token-based Header Authentication for WebSockets behind Node.js

Using Node.JS to proxy requests to mutate them under the hood can be beneficial. In cases like these, it can also make your product more secure.

VIDEO: Building API's with Django and GraphQL

At our last Django Meetup Group event, Jayden Windle, the lead engineer at Jetpack, an on demand delivery company, talks building APIs with Django and GraphQL. Watch the video to learn more.

Browse all Blog Articles

Ready for your new product adventure?

Let's Get Started