Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Facebook Auth with AngularJS and Django REST Framework

I am developing a SPA application with AngularJS which uses Django backend for the server. The way that I communicate with the server from the SPA is with django-rest-framework. So now I want to make authentication with facebook (google and twitter too) and I read a lot on this topic and found OAuth.io which is making the authetication on the client SPA side and python-social-auth which is doing the same thing but on the server side.

So currently I have only the client auth, my app is connecting to facebook (with OAuth.io) and login successfully. This process is returning access_token and then I am making a request to my API which have to login this user or create account for this user by given token and this part is not working. So I am not sure where I am wrong, maybe because there isn't a full tutorial about using python-social-auth so maybe I am missing something or.. I don't know..

So some code of this what I have:

On the SPA side: This is the connection with OAuth.io and is working because I am getting the access token. Then I have to make a request to my rest API. backend is 'facebook', 'google' or 'twitter'

OAuth.initialize('my-auth-code-for-oauthio'); OAuth.popup(backend, function(error, result) {     //handle error with error     //use result.access_token in your API request      var token = 'Token ' + result.access_token;     var loginPromise = $http({          method:'POST',           url: 'api-token/login/' + backend + '/',           headers: {'Authorization': token}});           loginPromise.success(function () {              console.log('Succeess');          });          loginPromise.error(function (result) {              console.log('error');          }); }); 

On the server in my settings.py I have added social plugin to the installed apps, template context preprocessors, some auth backends and that is my file:

INSTALLED_APPS = (     'django.contrib.auth',     'django.contrib.contenttypes',     'django.contrib.sessions',     ...,     'rest_framework',     'rest_framework.authtoken',     'api',     'social.apps.django_app.default',     'social' ) TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",                                "django.core.context_processors.debug",                                "django.core.context_processors.i18n",                                "django.core.context_processors.media",                                "django.core.context_processors.static",                                "django.core.context_processors.request",                                "django.contrib.messages.context_processors.messages",                                'social.apps.django_app.context_processors.backends',                                'social.apps.django_app.context_processors.login_redirect',)  REST_FRAMEWORK = {     'DEFAULT_AUTHENTICATION_CLASSES': (         'rest_framework.authentication.TokenAuthentication',     ) }  SOCIAL_AUTH_FACEBOOK_KEY = 'key' SOCIAL_AUTH_FACEBOOK_SECRET = 'secret' SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']  AUTHENTICATION_BACKENDS = (       'social.backends.open_id.OpenIdAuth',       'social.backends.facebook.FacebookOAuth2',       'social.backends.facebook.FacebookAppOAuth',       'social.backends.google.GoogleOpenId',       'social.backends.google.GoogleOAuth2',       'social.backends.google.GoogleOAuth',       'social.backends.twitter.TwitterOAuth',       'django.contrib.auth.backends.ModelBackend',   ) 

In my views.py of the API I have the following (I found it here):

from django.contrib.auth.models import User, Group from rest_framework import viewsets, generics from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import authentication, permissions, parsers, renderers from rest_framework.authtoken.serializers import AuthTokenSerializer from rest_framework.decorators import api_view, throttle_classes from social.apps.django_app.utils import strategy from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly  from django.contrib.auth import get_user_model from django.db.models.signals import post_save from django.dispatch import receiver from rest_framework.authtoken.models import Token  class ObtainAuthToken(APIView):     throttle_classes = ()     permission_classes = ()     parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)     renderer_classes = (renderers.JSONRenderer,)     serializer_class = AuthTokenSerializer     model = Token      # Accept backend as a parameter and 'auth' for a login / pass     def post(self, request, backend):         serializer = self.serializer_class(data=request.DATA)          if backend == 'auth':             if serializer.is_valid():                 token, created = Token.objects.get_or_create(user=serializer.object['user'])                 return Response({'token': token.key})             return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)          else:             # Here we call PSA to authenticate like we would if we used PSA on server side.             user = register_by_access_token(request, backend)              # If user is active we get or create the REST token and send it back with user data             if user and user.is_active:                 token, created = Token.objects.get_or_create(user=user)                 return Response({'id': user.id , 'name': user.username, 'userRole': 'user','token': token.key})  @strategy() def register_by_access_token(request, backend):     backend = request.strategy.backend     user = request.user     user = backend._do_auth(         access_token=request.GET.get('access_token'),         user=user.is_authenticated() and user or None     )     return user 

And finally I have these routes in urls.py:

... url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token'), url(r'^api-token/login/(?P<backend>[^/]+)/$', views.ObtainAuthToken.as_view()), url(r'^register/(?P<backend>[^/]+)/', views.register_by_access_token), ... 

Everytime when I try to do auth, OAuth.io is working and the rqest to api returns

detail: "Invalid token"

I think that I missed something in the configuration of python-social-auth or I am doing everything wrong. So I will be glad if anyone has some ideas and want to help :)

like image 915
valkirilov Avatar asked Dec 26 '13 14:12

valkirilov


Video Answer


1 Answers

Add the following line to your ObtainAuthToken class

authentication_classes = () 

and your error {"detail": "Invalid token"} will go away.

Here's why...

Your request contains the following header

Authorization: Token yourAccessToken 

yet you have defined rest_framework.authentication.TokenAuthentication in DEFAULT_AUTHENTICATION_CLASSES.

Based on this Django thinks you want to perform token authentication as you have passed a Token in. It fails because this is an access token for facebook and doesn't exist in your django *_token database, hence the invalid token error. In your case all you need to do is tell Django not to use TokenAuthentication for this view.

FYI

Keep in mind you may encounter further errors as your code execution was halted before the post method of ObtainAuthToken executed. Personally when trying to step through your code I got the error

'DjangoStrategy' object has no attribute 'backend' 

on

backend = request.strategy.backend 

and resolved it by changing to

uri = '' strategy = load_strategy(request) backend = load_backend(strategy, backend, uri) 

Additionally you should update your you register_by_access_token function as it doesn't line up with the working code from the blog you referenced. The blog author posted his latest code here. Your version doesn't pull the token out of the auth header which is required if you want to use it to auth with a third party like facebook.

like image 153
Steven Avatar answered Sep 21 '22 13:09

Steven