Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python-social-auth not getting correct Google OAuth2 details

I want to login a user using the python-social-auth functionality for Google Plus signin in Django. When logging in from my website, everything works fine and the correct details are added to the database.

However, I want to authenticate from my Android application as well. The user logs in in the application, which then sends the access token to the django API, which handles the login process in the following code, adapted from the documentation:

@csrf_exempt
@serengeti_api_request
@psa('social:complete')
def login_social_token(request, backend):
    # Ensure the token has been specified.
    token = request.META.get('HTTP_ACCESSTOKEN')
    if token is None:
        raise SerengetiApiRequestException('Access token is missing!')

    # Login the user for this session
    user = request.backend.do_auth(token)
    if user is None:
        raise SerengetiApiRequestException('Could not authenticate user!')

    login(request, user)

    # Store the email address if one has been specified (e.g. Twitter)
    email = request.META.get('HTTP_EMAIL')
    if email is not None:
        user.email = email
        user.save()

    # Prepare the parameters to be returned
    response = dict({
        'id': user.id,
        'first_name': user.first_name,
        'last_name': user.last_name,
        'api_key': request.session.session_key,
    })

    # Return a 200 status code to signal success.
    return HttpResponse(json.dumps(response, indent=4), status=200)

When logging in from the website, the social_auth_usersocialauth table contains:

id | provider      | uid       | extra_data
==========================================
10 | google-oauth2 | <myemail> | {"token_type": "Bearer", "access_token": "<token>", "expires": 3600}

However, when logging in from the application using the above function, the operation completes ok, but the entry in the table looks like this:

id | provider      | uid     | extra_data
=========================================
10 | google-oauth2 | <empty> | {"access_token": "", "expires": null}

Also, the auth_user table contains a username like eeed494412obfuscated48bc47dd9b instead of the Google Plus username and the email field is empty.

What am I doing wrong and how can I obtain the same functionality as I get on the website?

I would like to mention that I have implemented Facebook and Twitter authentication from the Android application, which call the above-mentioned function and store the correct details, only Google Plus is causing problems.

like image 806
Vlad Schnakovszki Avatar asked Jan 30 '15 14:01

Vlad Schnakovszki


People also ask

What is the Google OAuth URL?

Auth URL: https://accounts.google.com/o/oauth2/auth. Access Token URL: https://accounts.google.com/o/oauth2/token. Client ID: Retrieved from the Google Console for your Google Classroom project or the credentials. json file that you may have set under your repository if you're coding against this API.

What is Python Social Auth?

Python Social Auth aims to be an easy-to-setup social authentication and authorization mechanism for Python projects supporting protocols like OAuth (1 and 2), OpenID and others.


1 Answers

Just wanted to share an alternative way of doing this. This example is quite primitive and doesn't cover all cases (e.g. failed authentication). However, it should give enough insight into how OAuth2 authentication can be done.

Obtain CLIENT ID

Obtain a CLIENT ID from OAuth2 service provider (e.g. Google) and configure redirect URLs.

I assume you have already done this.

Create a login / registration link

You need to generate a login / registration link in your view. It should be something like this:

https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={{CLIENT_ID}}&redirect_uri={{REDIRECT_URL}}&scope=email

Replace {{CLIENT_ID}} and {{REDIRECT_URL}} with the details you obtained in the previous step.

Create a new view

In urls.py add something like:

url(r'^oauth2/google/$', views.oauth2_google),

In your views.py create a method:

def oauth2_google(request):

    # Get the code after a successful signing
    # Note: this does not cover the case when authentication fails
    CODE = request.GET['code']

    CLIENT_ID = 'xxxxx.apps.googleusercontent.com' # Edit this
    CLIENT_SECRET = 'xxxxx' # Edit this
    REDIRECT_URL = 'http://localhost:8000/oauth2/google' # Edit this

    if CODE is not None:
        payload = {
            'grant_type': 'authorization_code', 
            'code': CODE, 
            'redirect_uri': REDIRECT_URL, 
            'client_id': CLIENT_ID, 
            'client_secret': CLIENT_SECRET
            }

        token_details_request = requests.post('https://accounts.google.com/o/oauth2/token', data=payload)
        token_details = token_details_request.json()
        id_token = token_details['id_token']
        access_token = token_details['access_token']

        # Retrieve the unique identifier for the social media account
        decoded = jwt.decode(id_token, verify=False)
        oauth_identifier = decoded['sub']

        # Retrieve other account details
        account_details_request = requests.get('https://www.googleapis.com/plus/v1/people/me?access_token=' + access_token)
        account_details = account_details_request.json()
        avatar = account_details['image']['url']
        
        # Check if the user already has an account with us
        try:
            profile = Profile.objects.get(oauth_identifier=oauth_identifier)
            profile.avatar = avatar
            profile.save()
            user = profile.user
        except Profile.DoesNotExist:
            user = User.objects.create_user()           
            user.save()
            profile = Profile(user=user, oauth_identifier=oauth_identifier, avatar=avatar)
            profile.save()

        user.backend = 'django.contrib.auth.backends.ModelBackend'
        login(request, user)

        return redirect('/')

You might need the following imports:

from django.shortcuts import redirect
import jwt # PyJWT==0.4.1
import requests # requests==2.5.0
import json
like image 190
martynas Avatar answered Oct 12 '22 06:10

martynas