我如何使用Django OAuth工具包与Python社会认证?

我正在使用Django Rest Framework构建一个API。 后来这个API应该被iOS和Android设备使用。 我想允许我的用户使用Facebook和Google等oauth2提供商注册。 在这种情况下,他们不应该在我的平台上创建帐户。 但是,如果用户没有使用django-oauth-toolkit的Facebook / Google帐户,那么用户也应该可以注册,所以我拥有自己的oauth2提供商。

对于外部提供商,我使用python-social-auth,它可以正常工作并自动创建用户对象。

我希望客户端通过使用持票人令牌进行身份验证,这对于与我的提供者签约的用户来说工作良好(django-oauth-toolkit为Django REST Framework提供了身份验证方案和权限类)。
但是,python-social-auth仅实现基于会话的身份验证,因此没有直接的方法来代表由外部oauth2提供程序注册的用户进行身份验证的API请求。

如果我使用由django-oauth-toolkit生成的access_token,请执行如下操作:

curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/

但是,以下方法不起作用,因为Django REST Framework没有相应的认证方案,python-social-auth提供的AUTHENTICATION_BACKENDS仅适用于基于会话的认证:

curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/

在使用python-social-auth进行身份验证后,使用Django REST Framework提供的可浏览API工作得很好,只有没有会话cookie的API调用不起作用。

我想知道这个问题最好的办法是什么。 我看到它的方式基本上有两种选择:

答:当用户使用外部oauth2提供程序注册(由python-social-auth处理)时,挂钩进程以创建oauth2_provider.models.AccessToken并继续使用'oauth2_provider.ext.rest_framework.OAuth2Authentication' ,现在进行身份验证还有向外部提供商注册的用户。 此处建议使用此方法:https://groups.google.com/d/msg/django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ

B:使用python-social-auth进行API请求认证。 我可以通过编写一个自定义后端并使用register_by_access_token将我自己的用户变成python-social-auth。 但是,由于API调用不能使用Django会话,这意味着我必须为Django Rest Framework编写一个验证方案,该方案利用python-social-auth存储的数据。 关于如何做到这一点的一些指示可以在这里找到:
http://psa.matiasaguirre.net/docs/use_cases.html#signup-by-oauth-access-token
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-django.html
然而,我理解python-social-auth的方式只是在进行登录时验证令牌,然后依赖于Django会话。 这意味着我必须找到一种方法来防止python-social-auth为每个无状态API请求执行整个oauth2-flow,而是检查存储在数据库中的数据,因为这些数据并非真正针对查询进行了优化存储为JSON(我可以使用UserSocialAuth.objects.get(extra_data__contains =)虽然)。
我还必须考虑验证访问令牌的范围,并使用它们来检查权限,django-oauth-toolkit已经做了一些事情( TokenHasScoperequired_scopes等)。

目前,我倾向于使用选项A,因为django-oauth-toolkit提供了与Django Rest Framework的良好集成,并且获得了我需要的一切。 唯一的缺点是我必须将由python-social-auth检索的access_tokens“注入”到django-oauth-toolkit的AccessToken模型中,这种模式在某种程度上感觉不对,但可能是迄今为止最简单的方法。

有没有人反对这样做,或者可能以不同的方式解决了同样的问题? 我是否缺少明显的东西,让我的生活比必要的更难? 如果有人已经将django-oauth-toolkit与python-social-auth和外部oauth2提供程序集成,我将非常感谢一些提示或观点。


很多实现OAuth的难点归结为理解授权流程应该如何工作。 这主要是因为这是登录的“出发点”,并且在与第三方后端(使用Python社交身份验证之类的东西)一起工作时,您实际上是这样做了两次:一次是针对您的API,一次是针对第三方API。

使用您的API和第三方后端授权请求

您需要的认证过程是:

选项A的序列图

Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app

我在这里使用“Facebook”作为第三方后端,但是对于任何后端来说,这个过程都是一样的。

从移动应用程序的角度来看, 您只能重定向到由Django OAuth Toolkit提供的/authorize url 。 从那里开始,移动应用程序会一直等到达到回叫网址,就像在标准的OAuth授权流程中一样。 几乎所有其他事情(Django登录,社交登录等)都由后台中的Django OAuth Toolkit或Python Social Auth处理。

这也将与您使用的任何OAuth库几乎兼容,并且无论使用哪个第三方后端,授权流程都可以工作。 它甚至会处理需要能够支持Django身份验证后端(电子邮件/用户名和密码)以及第三方登录的(常见)情况。

选项A没有第三方后端

Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app

此处还要注意的一点是,移动应用程序(可以是任何OAuth客户端) 永远不会收到Facebook /第三方OAuth令牌 。 这非常重要,因为它可以确保您的API充当OAuth客户端与您用户的社交帐户之间的中介。

以您的API作为网守的序列图

Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name

否则,OAuth客户端将能够绕过您的API并代表您向第三方API发出请求。

绕过您的API的序列图

Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data

您会注意到,此时您将失去对第三方令牌的全部控制权 。 这是特别危险的,因为大多数代币都可以访问各种数据,从而打开滥用的大门,并最终以您的名义下落。 很可能,那些登录到您的API /网站的用户不打算与OAuth客户端共享他们的社交信息,而是期望您保留这些信息(尽可能保密),但是却将这些信息暴露给每个人

验证对您的API的请求

当移动应用程序使用您的OAuth令牌向您的API发出请求时,所有身份验证都会在后台通过Django OAuth Toolkit(或您的OAuth提供程序)进行。 你看到的只是有一个User与你的请求相关联。

OAuth令牌如何验证

Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back

这很重要,因为在授权阶段之后,如果用户来自Facebook或Django的身份验证系统,则不会产生影响 。 您的API只需要User使用,而您的OAuth提供程序应该能够处理令牌的身份验证和验证。

这与使用会话支持的身份验证时Django REST框架如何对用户进行身份验证没有多大区别。

使用会话进行身份验证的序列图

Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data

同样, 所有这些都由Django OAuth Toolkit处理,不需要额外的工作来实现。

使用原生SDK

在大多数情况下,您将通过自己的网站对用户进行身份验证,并使用Python Social Auth处理所有事情。 但是一个明显的例外是使用本地SDK,因为认证和授权是通过本地系统处理的 ,这意味着您完全绕过了您的API 。 对于需要使用第三方登录的应用程序或根本不使用您的API的应用程序来说,这非常适合,但这两者在一起时是一场噩梦

这是因为您的服务器无法验证登录名 ,并被迫假设登录名是有效且真实的 ,这意味着它会绕过Python社交身份验证为您提供的任何和所有安全性。

使用本机SDK可能会导致问题

Mobile App -> Facebook SDK : Opens the authorization prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user

您会注意到这会在身份验证阶段跳过您的API,然后强制您的API对传入的令牌进行假设。但肯定遇到此风险可能值得的情况 ,因此您应该先评估一下把它扔掉。 这是对用户的快速登录和本地登录之间的折衷,并可能处理不良或恶意令牌


我通过使用A.选项来解决它。

我所做的是注册使用第三方的用户通过他们的第三方访问令牌进行注册。

url(r'^register-by-token/(?P<backend>[^/]+)/$',
    views.register_by_access_token),

这样,我可以发出这样的GET请求:

GET http://localhost:8000/register-by-token/facebook/?access_token=123456

并且register_by_access_token被调用。 request.backend.do_auth将向提供者查询来自令牌的用户信息,并且如果他已经注册,则向用户注册具有信息或登录的用户帐户。

然后,我手动创建一个令牌并将其作为JSON返回,以便让客户端查询我的API。

from oauthlib.common import generate_token
...
@psa('social:complete')
def register_by_access_token(request, backend):
    # This view expects an access_token GET parameter, if it's needed,
    # request.backend and request.strategy will be loaded with the current
    # backend and strategy.
    third_party_token = request.GET.get('access_token')
    user = request.backend.do_auth(third_party_token)

    if user:
        login(request, user)

        # We get our app!   
        app = Application.objects.get(name="myapp")

        # We delete the old token
        try:
            old = AccessToken.objects.get(user=user, application=app)
        except:
            pass
        else:
            old.delete()

        # We create a new one
        my_token = generate_token()

        # We create the access token 
        # (we could create a refresh token too the same way) 
        AccessToken.objects.create(user=user,
                                   application=app,
                                   expires=now() + timedelta(days=365),
                                   token=my_token)

        return "OK" # you can return your token as JSON here

    else:
        return "ERROR"

我只是不确定我产生令牌的方式,这是一种好的做法吗? 那么,在此期间,它的工作原理!


也许django-rest-framework-social-oauth2是你正在寻找的。 这个包依赖于你已经使用的python-social-authdjango-oauth-toolkit 。 我通过文档快速扫描,它似乎只是实现你正在尝试做的事情。

链接地址: http://www.djcxy.com/p/33787.html

上一篇: How can I use Django OAuth Toolkit with Python Social Auth?

下一篇: Basic HTTP authentication with the Heroku Platform API