Want to integrate social auth with Django REST Framework instead? See our complementary piece updated to work with DRF.
Recently for an iOS application we were developing, we needed to allow users to sign up with Facebook, Twitter, or create their own login with an email address. Our backend and API were developed using Django and Tastypie respectively
Python Social Auth is intended for use through the web and has routes already set up for authenticating and disconnecting users from different social media services. It also has tons of different pluggable backends that you can just use out of the box. Unfortunately I could not find any documentation or tutorials on how I could call into the Python Social Auth codebase without using the auto-magicness of the url routes that come with the package.
The first thing you need to understand about Python Social Auth is its pipeline concept. Here is the default pipeline:
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
Whenever a user is authenticated, each one of these functions is called in turn. The general workflow is to figure out if the user is authenticated, get their details from the social media provider, create a new UserSocialAuth and User record, and finally fill in any extra details about this user that you've received from the social media provider.
The pipeline is also entirely configurable. This means that you can override this in your settings.py and create your own pipeline functions that do whatever you'd like. For example, we've created a pipeline step that gets the user's profile image from either Facebook or Twitter and saves that in our application.
def get_profile_image(strategy, details, response, uid, user, social, *args, **kwargs):
"""Attempt to get a profile image for the User"""
if user is None:
return
image_url = None
if strategy.backend.name == "facebook":
image_url = "https://graph.facebook.com/{0}/picture?type=large".format(uid)
elif strategy.backend.name == "twitter":
if response['profile_image_url'] != '':
image_url = response['profile_image_url']
if image_url:
try:
result = urllib.urlretrieve(image_url)
user.original_photo.save("{0}.jpg".format(uid), File(open(result[0])))
user.save(update_fields=['original_photo'])
except URLError:
pass
Strategies in Python Social Auth are the different frameworks that Python Social Auth supports. Originally we used Django Social Auth on past projects, and it was merged into this one so that code reuse could happen between multiple frameworks (Flask, Django, CherryPy, Pyramid, etc).
Each Strategy basically has framework specific code to plug into Python Social Auth's functionality seamlessly into the framework. We'll obviously be using the Django Strategy to accomplish this integration.
Python Social Auth comes with a whole lot of backends out of the box. It is also easy to create your own backend if you're trying to integrate with a less common social media provider. For our integration and example we were integrating with Facebook + Twitter, but this solution is pretty agnostic to whatever backend you're using.
To note, in our solution we call backends "providers" or "social media providers", which I've mentioned above previously. This is just so that the iOS application can inform the server which backend the user is trying to authenticate with.
To review:
1. We pick the Strategy or framework specific code we're trying to implement Python Social Auth with, which is Django in this example.
2. We set up the Pipeline, or chain of functions that will be called in succession to authenticate the user with the Backend of our choice then create a Django User in our application.
Once you have a grasp of these basic concepts, it is actually pretty easy to hook into Python Social Auth programmatically. All we need to do is load the Django Strategy with the Backend we're currently authenticating it with, then tell it to start the Pipeline.
from social.apps.django_app import load_strategy
provider = “facebook”
access_token = “CAAIkpON595IBADC8CqXgq615wfsls15u6RI23sreqbzntau”
strategy = load_strategy(backend=provider)
user = strategy.backend.do_auth(access_token)
To put this in a Tastypie Resource all we have to do is overwrite the obj_create function to do the above logic and make sure we return the user we've just created in the bundle. Here's a simple example of what that would look like.
class SocialSignUpResource(BaseModelResource):
class Meta:
queryset = User.objects.all()
allowed_methods = ['post']
authentication = Authentication()
authorization = Authorization()
resource_name = "social_sign_up"
def obj_create(self, bundle, request=None, **kwargs):
provider = bundle.data['provider']
access_token = bundle.data['access_token']
strategy = load_strategy(backend=provider)
user = strategy.backend.do_auth(access_token)
if user and user.is_active:
bundle.obj = user
return bundle
else:
raise BadRequest("Error authenticating user with this provider")
To make a request to this resource to sign a user up would be very straightfoward. Here's the example JSON.
{
"provider":"facebook",
"access_token":"CAAIkpON595IBADC8CqXgq615wfsl"
}