Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django-rest-auth: social login with google

The django-rest-auth documentation discusses Facebook integration, which I am not interested in-- my concern is providing social login via Google. I have tried this for quite some time now and I'm wondering if anyone else has any documentation on how they did this...even just a rough sketch would be helpful. So far, I have not found any results for this search. I am almost there, but cannot get it to work with the Django rest framework (DRF) browsable API.

Here is what I have so far: I started from the demo project supplied on the django-rest-auth github page and modified the social login template HTML page to only require the 'code' input, not both 'code' AND 'access_token'. When I supply a valid code (obtained by a separate request to google's auth endpoint), this works fine; the browsable API renders the usual web page with the 'key' (my application's API token for the user) in the response. Checking the django admin, everything worked- the user is logged in, email is authenticated, etc. Good so far.

The issue is that starting point of supplying the 'code'- and how I get that code from google in the first place. When I previously (successfully) used the allauth package, I could simply click on a link, which would "invisibly" perform the whole OAuth2 flow (i.e. request the code, use that code to get the access token, and use the access token to get user's google account info).

To recreate that seamless flow (i.e. NOT starting out with the code), I figured I could interrupt the OAuth2 flow and "intercept" the code returned from google, and then POST that code to the rest-auth social login API. To that end, I created a custom allauth.socialaccount.providers.oauth2.views.OAuth2CallbackView by overriding the dispatch method:

class CustomOAuth2CallbackView(OAuth2CallbackView):
    def dispatch(self, request):
        # gets the code correctly:
        code = request.GET['code']

        # rp is of type requests.methods.Response
        rp = requests.post(<REST-AUTH API ENDPOINT>, data = {'code':code})
        return rp

Usually, this method is called when google sends a GET request to the callback uri you initially supply to google's auth endpoint. With this override, I am able to successfully parse the code returned from google in that callback. The POST request works and has the user's key in the resp._content field. However, it ultimately fails to produce the intended view in the DRF browsable API.

Based on diving down in the callstack, I find that rest_framework.views.APIView.dispatch returns an object of type rest_framework.response.Response. However, when the requests.post method used above completes, it returns an instance of type requests.models.Response. As a result, it does not have the proper attributes and fails in the django middleware. For example, it has no acceptable_renderer attribute and no 'get' method (which is used in django.middleware.clickjacking.py). I could, conceivably, add these requirements to the requests.models.Response (rp) instance, but then this hack becomes even more of a kludge.

Thanks for any help you can provide!

like image 339
blawney_dfci Avatar asked Dec 24 '15 01:12

blawney_dfci


People also ask

How does Django integrate social login?

Django Setup Set up the initial tables and add a superuser: $ python manage.py migrate Operations to perform: Apply all migrations: admin, contenttypes, auth, sessions Running migrations: Applying contenttypes. 0001_initial... OK Applying auth. 0001_initial... OK Applying admin.

Which authentication is best in Django REST framework?

Django-Knox is a framework that makes the authentication of the API endpoints built with the Django Rest Framework easier. However, Knox is also a token-based authentication like JSON Web Token (JWT) auth. Django-Knox comes with well-detailed documentation for easy implementation.


1 Answers

https://github.com/st4lk/django-rest-social-auth

class SocialLoginSignup(SocialSessionAuthView):
"""
    Mobile user social signup and login api view

    args:
        provider: name of the social network
        access_token: auth token got from the social sites
"""
serializer_class = SocialSignUpSerializer
authentication_classes = (TokenAuthentication,)

def post(self, request, *args, **kwargs):
    serializer = self.serializer_class(data=request.data)
    serializer.is_valid(raise_exception=True)
    provider_name = serializer.validated_data['provider']

    decorate_request(request, provider_name) # assign the provider class object in request

    authed_user = request.user if not request.user.is_anonymous() else None

    token = serializer.validated_data['access_token']

    if self.oauth_v1() and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in serializer.validated_data:
        request_token = parse_qs(request.backend.set_unauthorized_token())
        return Response(request_token)

    try:
        # authentication function get call from assign class object in request
        user = request.backend.do_auth(token, user=authed_user)
    except social_exceptions.AuthException as e:
        raise exceptions.ParseError({'error':str(e)})
    except social_exceptions.AuthTokenError as e:
        raise exceptions.ParseError({'error': str(e)})
    except social_exceptions.AuthAlreadyAssociated as e:
        raise exceptions.ParseError({'error': str(e)})
    except social_exceptions.AuthFailed as e:
        raise exceptions.ParseError({'error':str(e)})
    except social_exceptions.AuthUnknownError as e:
        raise exceptions.ParseError({'error': str(e)})
    except social_exceptions.WrongBackend as e:
        raise exceptions.ParseError({'error':str(e)})
    except Exception as e:
        raise exceptions.ParseError({'error': social_message.INVALID_AUTH_TOKEN})

    token, created = Token.objects.get_or_create(user=user)
    return Response({'auth_token':token.key})
like image 88
aman kumar Avatar answered Oct 06 '22 07:10

aman kumar