Необходимы ли сеансы для python-social-auth

Я создаю приложение django с бэкэндом API (с DRF) и клиентом angularjs. Моя цель - полностью отключить сервер и клиент, используя JWT вместо сеансов. Я пытаюсь интегрировать python-social-auth (PSA) с django-rest-framework-jwt (DRFJWT), поэтому мой Цель состоит в том, чтобы иметь что-то для этого:

Журналы пользователя с электронной почтой /facebook через angular client → форму сообщений клиента в URL-адрес PSA → Вход в систему PSA/создание пользователя → [!] DRFJWT создает токен, который затем отправляет обратно в клиентский → токен хранит клиента локальное хранилище затем использует токен для каждого запроса

[!]: В настоящее время я боюсь. Я думаю, что я могу изменить метод do_complete в PSA, например,

from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler


def do_complete(backend, login, user=None, redirect_name='next',
            *args, **kwargs):
  # pop redirect value before the session is trashed on login()
  data = backend.strategy.request_data()
  redirect_value = backend.strategy.session_get(redirect_name, '') or \
                 data.get(redirect_name, '')

  is_authenticated = user_is_authenticated(user)
  user = is_authenticated and user or None

  partial = partial_pipeline_data(backend, user, *args, **kwargs)
  if partial:
      xargs, xkwargs = partial
      user = backend.continue_pipeline(*xargs, **xkwargs)
  else:
      user = backend.complete(user=user, *args, **kwargs)

  if user_is_active(user):
      # catch is_new/social_user in case login() resets the instance
      is_new = getattr(user, 'is_new', False)
      social_user = user.social_user
      login(backend, user, social_user)

  payload = jwt_payload_handler(user)
  return { 'token': jwt_encode_handler(payload) }

Это единственный способ сделать то, что я пытаюсь выполнить?

Мне также интересно, хорошо ли с точки зрения лучшей практики использовать сеансы для управления конвейером и JWT для auth?

Ответы

Ответ 1

Я также использую python-social-auth и django-rest-framework-jwt для аутентификации пользователей.

То, как мне удалось объединить две системы аутентификации, было создание пользовательского представления, которое принимает " access_token", предоставляемый поставщиком oAuth, и пытается создать с ним нового пользователя. После создания пользователя вместо возвращения аутентифицированного пользователя/сеанса я возвращаю токен JWT.

Ниже описаны фрагменты кода.

Back-End

В моем файле views.py я включил следующее:

@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)

В моем urls.py я включил следующий маршрут:

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

Front-End

Теперь, когда аутентификация по серверу подключена, вы можете использовать любую библиотеку интерфейса для отправки access_token и аутентификации пользователя. В моем случае я использовал AngularJS.

В файле контроллера я вызываю API так:

/**
* This function gets called after successfully getting the access_token from Facebook 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);
    });
}

При таком подходе вы можете смешивать два плагина. Все отправленные токены будут поступать из django-rest-framework-jwt, хотя пользователи все равно могут аутентифицироваться с помощью тех, которые предоставляются сайтами, такими как Facebook, Google, Twitter и т.д.

Я только показал подход к аутентификации через Facebook, однако вы можете следовать аналогичному подходу для других поставщиков.

Ответ 2

Нет, вам не нужно использовать сеансы (стандартную систему входа в Django) с помощью python-social-auth. Что нужно сделать, чтобы JWT и PSA работали вместе, это DRF.

Здесь мое решение:

Я использовал стандартный URL-адрес PSA для того, чтобы сделать запрос слишком социальным /login/(?P<backend>[^/]+)/$, изменил URL-адрес в urls.py, чтобы перенаправить с Facebook/Twitter на мой собственный.

url(r'^complete/(?P<backend>[^/]+)/$', views.SocialAuthViewComplete.as_view()),

Точка использования API - это доступ к пользовательским данным в запросе, который делает PSA. DRF позволяет это сделать, если у вас есть аутентификация JWT в DEFAULT_AUTHENTICATION_CLASSES

REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': (
              'rest_framework.authentication.SessionAuthentication',
              'rest_framework.authentication.TokenAuthentication',
              'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),}

В views.py

from social.apps.django_app.views import complete

class SocialAuthViewComplete(APIView):
    permission_classes = ()

    def post(self, request, backend, *args, **kwargs):
        try:
            #Wrap up  PSA `complete` method.    
            authentication = complete(request, backend, *args, **kwargs)
        except Exception, e:
            exc = {
                'error': str(e)
            }
            return Response(exc, status=status.HTTP_400_BAD_REQUEST)
        return Response({'data': authentication}, status=status.HTTP_202_ACCEPTED)

Затем я модифицированный метод do_complete в PSA:

def do_complete(backend, login, user=None, redirect_name='next',
                *args, **kwargs):
    # pop redirect value before the session is trashed on login()
    data = backend.strategy.request_data()
    redirect_value = backend.strategy.session_get(redirect_name, '') or \
                     data.get(redirect_name, '')

    is_authenticated = user_is_authenticated(user)
    user = is_authenticated and user or None

    partial = partial_pipeline_data(backend, user, *args, **kwargs)
    if partial:
        xargs, xkwargs = partial
        user = backend.continue_pipeline(*xargs, **xkwargs)
    else:
        user = backend.complete(user=user, *args, **kwargs)

    user_model = backend.strategy.storage.user.user_model()
    if user and not isinstance(user, user_model):
        return user

    if is_authenticated:
        if not user:
            information =  'setting_url(backend, redirect_value, LOGIN_REDIRECT_URL'
        else:
            information =  'setting_url(backend, redirect_value, NEW_ASSOCIATION_REDIRECT_URL,LOGIN_REDIRECT_URL'
    elif user:
        # Get the JWT payload for the user.
        payload = jwt_payload_handler(user)

        if user_is_active(user):
            is_new = getattr(user, 'is_new', False)
            if is_new:
                information = 'setting_url(backend, NEW_USER_REDIRECT_URL, redirect_value, LOGIN_REDIRECT_URL'
            else:
                information = 'setting_url(backend, redirect_value, LOGIN_REDIRECT_URL'
        else:
            return Response({
                        'status': 'Unauthorized',
                        'message': 'The user account is disabled.'
                    }, status=status.HTTP_401_UNAUTHORIZED)
    else:
        information = 'setting_url(backend, LOGIN_ERROR_URL, LOGIN_URL'


    return { 'an information i may use in future': information,
             'token': jwt_encode_handler(payload) # Create the response object with the JWT payload.
    }

I пробовал конвейеры и ассоциации пользователей, и он работает правильно. Также вы всегда можете изменить другой метод из PSA, если вам нужно, чтобы он работал с JWT.