Facebook Auth 与 AngularJS 和 Django REST 框架

Posted

技术标签:

【中文标题】Facebook Auth 与 AngularJS 和 Django REST 框架【英文标题】:Facebook Auth with AngularJS and Django REST Framework 【发布时间】:2014-01-14 04:44:36 【问题描述】:

我正在开发一个带有AngularJS 的SPA 应用程序,它使用Django 作为服务器后端。我从 SPA 与服务器通信的方式是使用django-rest-framework。所以现在我想用 facebook(谷歌和推特)进行身份验证,我阅读了很多关于这个主题的内容,发现 OAuth.io 正在客户端 SPA 端进行身份验证,python-social-auth 正在做同样的事情,但在服务器端。

所以目前我只有客户端身份验证,我的应用程序正在连接到 facebook(使用 OAuth.io)并成功登录。此过程正在返回 access_token,然后我向我的 API 发出请求,该 API 必须登录该用户或通过给定令牌为该用户创建帐户,这部分不起作用。所以我不确定我错在哪里,也许是因为没有关于使用 python-social-auth 的完整教程所以也许我遗漏了什么或者..我不知道..

所以我有一些代码:

在 SPA 方面:这是与 OAuth.io 的连接,并且正在工作,因为我正在获取访问令牌。然后我必须向我的其余 API 发出请求。后端是“facebook”、“google”或“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');
         );
);

在我的 settings.py 中的服务器上,我已将社交插件添加到已安装的应用程序、模板上下文预处理器、一些身份验证后端,这就是我的文件:

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

在 API 的 views.py 中,我有以下内容(我发现它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

最后我在 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),
...

每次我尝试进行身份验证时,OAuth.io 都在工作,并且 api 的 rqest 会返回

详细信息:“无效令牌”

我认为我在 python-social-auth 的配置中遗漏了一些东西,或者我做错了一切。因此,如果有人有一些想法并想提供帮助,我会很高兴:)

【问题讨论】:

看起来您在标头中传递令牌,但在register_by_access_token 上的request.GET 上查找它。 我看到了,但这是 here 的示例,但我无法理解 python-social-auth 的工作原理,因为没有示例如何使用它。所以也许我不会有社交身份验证..谢谢:) 您可以尝试此页面中的代码:blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api,它从标头中获取令牌。 是的,我知道可能为时已晚,但你解决了这个问题吗?我只设法获得未经授权的请求代码状态代码:401 UNAUTHORIZED @valkirilov,知道我们将如何集成 openid connect 吗? 【参考方案1】:

我和你一样使用工具,但我提供了我的登录/注册/.... django-allauth 包,然后使用django-rest-auth 进行API处理。

您只需要按照安装说明进行操作,然后将它们用于您的其余 API。

allauthrest-auth 添加到您的INSTALLED_APPS:

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

然后添加您的自定义网址:

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

最后,添加这一行:

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

这两个包就像一个魅力,你不需要关心任何类型的 login.registration,因为allauth 包处理 django 模型登录和 oAuth 登录。

希望对你有帮助

【讨论】:

好久不见,还是有问题!我发现 django-rest-auth 比这个线程中提到的其他选项更好。【参考方案2】:

是的。解决了。设置不对,需要添加权限。

 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'
     ]
 

还有一些关于管道的信息:

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

【讨论】:

【参考方案3】:

将以下行添加到您的ObtainAuthToken 类

authentication_classes = ()

您的错误 "detail": "Invalid token" 将消失。

这就是为什么...

您的请求包含以下标头

Authorization: Token yourAccessToken

但您已经在 DEFAULT_AUTHENTICATION_CLASSES 中定义了 rest_framework.authentication.TokenAuthentication。

基于这个 Django 认为你想要执行令牌身份验证,因为你已经传递了一个令牌。它失败了,因为这是 facebook 的访问令牌,并且在你的 django *_token 数据库中不存在,因此无效令牌错误。在您的情况下,您需要做的就是告诉 Django 不要为此视图使用 TokenAuthentication。

仅供参考

请记住,您可能会遇到更多错误,因为您的代码执行在 ObtainAuthToken 的 post 方法执行之前已停止。就个人而言,在尝试单步执行您的代码时出现错误

'DjangoStrategy' object has no attribute 'backend'

backend = request.strategy.backend

并通过更改为解决它

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

此外,您应该更新您的 register_by_access_token 函数,因为它与您引用的博客中的工作代码不一致。博客作者发布了他的最新代码here。如果您想使用它与 facebook 等第三方进行身份验证,您的版本不会将令牌从身份验证标头中提取出来。

【讨论】:

谢谢。 'DjangoStrategy' 对象没有属性 'backend' 提示很有帮助

以上是关于Facebook Auth 与 AngularJS 和 Django REST 框架的主要内容,如果未能解决你的问题,请参考以下文章

维护用户会话(在 Facebook O-Auth、服务器端登录之后)

Auth0 未定义。 angular-auth0, angularjs

flutter_facebook_auth:提供的身份验证凭据格式错误或已过期

social-auth 给出“错误”的 facebook id

AngularJS:未发送 X-Auth-Token(错误 401)

flutter_auth 要求 Facebook 登录的访问令牌是啥?