Eventually, from “Django Social Auth”, came “Python Social Auth” which is compatible with a number of other frameworks as well. This package is now the recommended one for integrating with social networks. If you have worked with Django Social Auth in the past, the setup and configuration is pretty straightforward. The major difference is in the package names (obviously!).
So, in one of my apps, I am using Facebook logins. And despite the inherent security risk (I am not going to elaborate here), I have been asked to implement the “associate by email address” feature. The idea is simple – if there is an user account already with the same email address of the social account being linked, connect them together.
Like Django Social Auth before, Python Social Auth has the same Pipeline architecture. The SOCIAL_AUTH_PIPELINE is a tuple of functions which are passed various data (when I say various, don’t get me wrong, it pretty much passes everything in the app). The data get passed to the first function, the return value of the first function get passed to the second – this way, it’s a pipeline of functions where one function’s return value is fed into another.
With that in mind, I just need to create a function that sits somewhere in the pipeline and checks for the email address returned from the social network. If any existing user accounts found, associate that as the user. So, I created a function in “core/utils.py” so that the full function identifier would be – “core.utils.associate_by_email”.
The contents are:
1 2 3 4 5 6 7 |
def associate_by_email(**kwargs): try: email = kwargs['details']['email'] kwargs['user'] = User.objects.get(email=email) except: pass return kwargs |
We are accepting all the arguments to the function using **kwargs. We’re then grabbing the email address from the “details” dictionary and setting the user if found. Being lazy, I surrounded the block with a try..except block for any exceptions which might arise.
Now, in settings, I added the pipeline like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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', 'core.utils.associate_by_email', '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' ) |
So, we set the user before we create an user. Thus if there is an existing user, we just update that user, otherwise, we create a new one.
I really love how simple and easy it is to extend the Python Social Auth package, no wonder it’s become the de-facto choice for social networks integration. 🙂