Auteur Facebook avec AngularJS et Django REST Framework

je développe une application SPA avec AngularJS qui utilise Django backend pour le serveur. La façon dont je communique avec le serveur de la SPA est avec django-repos-cadre. Donc maintenant je veux faire authentification avec facebook (google et twitter aussi) et j'ai lu beaucoup sur ce sujet et trouvé OAuth.io ce qui rend l'authentification du côté du client SPA et python-social-auth qui est en train de faire la même chose, mais sur le côté serveur.

donc actuellement je n'ai que le client auth, mon application se connecte à facebook (avec OAuth.io) et se connecter avec succès. Ce processus retourne access_token et ensuite je fais une demande à mon API qui doit se connecter à cet utilisateur ou créer un compte pour cet utilisateur par token donné et cette partie ne fonctionne pas. Donc je ne sais pas où je me trompe, peut-être parce qu'il n'y a pas de tutoriel complet sur l'utilisation de python-social-auth alors peut-être que je manque quelque chose ou.. Je ne sais pas..

Donc un peu de code de ce que j'ai:

Côté SPA: c'est la connexion avec OAuth.io et travaille parce que j'obtiens le jeton d'accès. Alors je dois faire une demande à mon API rest. backend est "facebook", "google" ou "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');
         });
});

sur le serveur dans mon settings.py j'ai ajouté le plugin social aux applications installées, préprocesseurs de contexte de template, certains auth backends et c'est mon fichier:

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',
  )

Dans mon views.py de l'API, j'ai le texte suivant (je l'ai trouvé ici):

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

et finalement j'ai ces routes en 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, outh.io fonctionne et le rqest renvoie

détails: "Invalid token"

je pense que j'ai manqué quelque chose dans la configuration de python-social-auth ou je fais tout de travers. Je vais donc être heureux si quelqu'un a des idées et qui veulent aider :)

27
demandé sur aendrew 2013-12-26 18:14:41

3 réponses

ajouter la ligne suivante à votre classe ObtainAuthToken

authentication_classes = ()

et votre erreur {"detail":" Invalid token"} disparaîtra.

Voici pourquoi...

Votre demande contient l'en-tête suivant

Authorization: Token yourAccessToken

pourtant vous avez défini rest_fram Framework.Authentication.TokenAuthentication in DEFAULT_AUTHENTICATION_CLASSES.

basé sur ce Django pense que vous voulez effectuer une authentification de token Jeton. Il échoue parce qu'il s'agit d'un token d'accès pour facebook et qu'il n'existe pas dans votre base de données django *_token, d'où l'erreur de token invalide. Dans votre cas, tout ce que vous devez faire est de dire à Django de ne pas utiliser TokenAuthentication pour cette vue.

pour information!--11-->

gardez à l'esprit que vous pouvez rencontrer d'autres erreurs car l'exécution de votre code a été interrompue avant l'exécution de la méthode post D'ObtainAuthToken. Personnellement en essayant de passer par votre code j'ai eu l'erreur

'DjangoStrategy' object has no attribute 'backend'

backend = request.strategy.backend

et résolu en changeant

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

en outre, vous devriez mettre à jour votre fonction you register_by_access_token car elle ne correspond pas au code de travail du blog que vous avez mentionné. L'auteur du blog a posté son dernier code ici. Votre version ne retire pas le token de l'en-tête auth qui est requis si vous voulez l'utiliser pour auth avec un tiers comme facebook.

7
répondu Steven 2014-09-17 12:09:59

Oui. Résoudre. Les paramètres ne sont pas corrects et vous devez ajouter des permissions.

 REST_FRAMEWORK = {
     # Use hyperlinked styles by default.
     # Only used if the `serializer_class` attribute is not set on a view.
     'DEFAULT_MODEL_SERIALIZER_CLASS':
         'rest_framework.serializers.HyperlinkedModelSerializer',

     # Use Django's standard `django.contrib.auth` permissions,
     # or allow read-only access for unauthenticated users.
     'DEFAULT_PERMISSION_CLASSES': [
         'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
     ]
 }

et quelques informations sur pipeline:

 SOCIAL_AUTH_PIPELINE = (
     'social.pipeline.social_auth.social_details',
     'social.pipeline.social_auth.social_uid',
     'social.pipeline.social_auth.auth_allowed',
     'social.pipeline.social_auth.social_user',
     'social.pipeline.user.get_username',
     'social.pipeline.social_auth.associate_by_email',
     'social.pipeline.user.create_user',
     'social.pipeline.social_auth.associate_user',
     'social.pipeline.social_auth.load_extra_data',
     'social.pipeline.user.user_details'
 )
4
répondu Aleksander Korovin 2014-12-13 16:27:47

j'utilise des outils comme vous, mais je fournis mon login/enregistrer/.... avec django-allauth package, puis utiliser django-rest-auth pour la manipulation de L'API.

vous avez juste besoin de suivre les instructions d'installation, puis de les utiliser pour vos API de repos.

ajouter allauth et rest-auth to your INSTALLED_APPS:

INSTALLED_APPS = (
    ...,
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth'
    ...,
    'allauth',
    'allauth.account',
    'rest_auth.registration',
    ...,
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
)

puis ajoutez vos urls personnalisées:

urlpatterns = patterns('',
    ...,
    (r'^auth/', include('rest_auth.urls')),
    (r'^auth/registration/', include('rest_auth.registration.urls'))
)

enfin, ajouter cette ligne:

TEMPLATE_CONTEXT_PROCESSORS = (
    ...,
    'allauth.account.context_processors.account',
    'allauth.socialaccount.context_processors.socialaccount',
    ...
)

Ces deux les paquets fonctionnent comme un charme, et vous n'avez pas besoin de vous soucier de n'importe quel type de login.l'inscription, parce que allauth package gère à la fois le login django model et le login oAuth.

j'espère que cela aide

1
répondu Moe Far 2015-07-01 07:04:27