Are sessions needed for python-social-auth

前端 未结 2 1949
慢半拍i
慢半拍i 2021-01-31 05:48

I\'m building a django app with an API backend(built with DRF) and angularjs client. My goal is to completely decouple the server and client using JWT in place of sessions. I\'m

2条回答
  •  没有蜡笔的小新
    2021-01-31 05:53

    I'm also using python-social-auth and django-rest-framework-jwt for user authentication.

    The way I was able to integrate the two authentication systems together was by creating a custom view that takes in the 'access_token' provided by the oAuth provider and attempts to create a new user with it. Once the user is created, instead of returning the authenticated user/session I return the JWT token.

    The following code snippets explain the solution.

    Back-End

    In my views.py file I included the following:

    @psa()
    def auth_by_token(request, backend):
        """Decorator that creates/authenticates a user with an access_token"""
        token = request.DATA.get('access_token')
        user = request.user
        user = request.backend.do_auth(
                access_token=request.DATA.get('access_token')
            )
        if user:
            return user
        else:
            return None
    
    class FacebookView(views.APIView):
        """View to authenticate users through Facebook."""
    
        permission_classes = (permissions.AllowAny,)
    
        def post(self, request, format=None):
            auth_token = request.DATA.get('access_token', None)
            backend = request.DATA.get('backend', None)
            if auth_token and backend:
                try:
                    # Try to authenticate the user using python-social-auth
                    user = auth_by_token(request, backend)
                except Exception,e:
                    return Response({
                            'status': 'Bad request',
                            'message': 'Could not authenticate with the provided token.'
                        }, status=status.HTTP_400_BAD_REQUEST)
                if user:
                    if not user.is_active:
                        return Response({
                            'status': 'Unauthorized',
                            'message': 'The user account is disabled.'
                        }, status=status.HTTP_401_UNAUTHORIZED)
    
                    # This is the part that differs from the normal python-social-auth implementation.
                    # Return the JWT instead.
    
                    # Get the JWT payload for the user.
                    payload = jwt_payload_handler(user)
    
                    # Include original issued at time for a brand new token,
                    # to allow token refresh
                    if api_settings.JWT_ALLOW_REFRESH:
                        payload['orig_iat'] = timegm(
                            datetime.utcnow().utctimetuple()
                        )
    
                    # Create the response object with the JWT payload.
                    response_data = {
                        'token': jwt_encode_handler(payload)
                    }
    
                    return Response(response_data)
            else:
                return Response({
                        'status': 'Bad request',
                        'message': 'Authentication could not be performed with received data.'
                }, status=status.HTTP_400_BAD_REQUEST)
    

    In my urls.py I included the following route:

    urlpatterns = patterns('',
        ...
        url(r'^api/v1/auth/facebook/', FacebookView.as_view()),
        ...
    )
    

    Front-End

    Now that the backend authentication is wired up, you can use any frontend library to send the access_token and authenticate the user. In my case I used AngularJS.

    In a controller file I call the API like so:

    /**
    * This function gets called after successfully getting the access_token from Facebook's API.
    */
    function successLoginFbFn(response) {
        var deferred = $q.defer();
        $http.post('/api/v1/auth/facebook/', {
            "access_token": response.authResponse.accessToken, 
            "backend": "facebook"
        }).success(function(response, status, headers, config) {
            // Success
            if (response.token) {
                // Save the token to localStorage and redirect the user to the front-page.
                Authentication.setToken(response.token);
                window.location = '/';
            }
            deferred.resolve(response, status, headers, config);
        }).error(function(response, status, headers, config) {
            // Error
            console.error('Authentication error.');
            deferred.reject(response, status, headers, config);
        });
    }
    

    With this approach you can mix the two plugins. All sent tokens will be coming from django-rest-framework-jwt even though users can still authenticate themselves with the ones provided by sites such as Facebook, Google, Twitter, etc.

    I only showed the approach to authenticate through Facebook, however you can follow a similar approach for other providers.

提交回复
热议问题