如何在Python Social Auth中使用Django OAuth Toolkit?

jev*_*ing 54 python django oauth-2.0 django-rest-framework python-social-auth

我正在使用Django Rest Framework构建API.之后这个API应该被iOS和Android设备使用.我希望允许我的用户注册Facebook和Google等oauth2提供商.在这种情况下,他们根本不必在我的平台上创建一个帐户.但是,如果没有Facebook/Google帐户,用户也应该能够注册,我正在使用django-oauth-toolkit,所以我有自己的oauth2-provider.

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

我希望客户端使用bearer令牌进行身份验证,这对于使用我的提供者注册的用户来说很好(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/
Run Code Online (Sandbox Code Playgroud)

但是,由于Django REST Framework没有相应的身份验证方案,并且python-social-auth提供的AUTHENTICATION_BACKENDS仅适用于基于会话的身份验证,因此以下情况不起作用:

curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/
Run Code Online (Sandbox Code Playgroud)

在使用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的工具包已经这样做(TokenHasScope,required_scopes等).

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

有没有人反对这样做或者可能以不同的方式处理同样的问题?我错过了一些明显的东西,让我的生活变得更加艰难吗?如果有人已经将django-oauth-toolkit与python-social-auth和外部oauth2提供程序集成,我将非常感谢一些指示或意见.

Kev*_*own 95

实现OAuth的许多困难归结为理解授权流程应该如何工作.这主要是因为这是登录的"起点",当使用第三方后端(使用像Python Social Auth 这样的东西)时,你实际上这样做了两次:一次用于你的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
Run Code Online (Sandbox Code Playgroud)

我在这里使用"Facebook"作为第三方后端,但任何后端的过程都是相同的.

从您的移动应用程序的角度来看,您只是重定向到/authorizeDjango OAuth Toolkit提供的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
Run Code Online (Sandbox Code Playgroud)

此处还需要注意的是,移动应用程序(可能是任何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
Run Code Online (Sandbox Code Playgroud)

否则,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
Run Code Online (Sandbox Code Playgroud)

您会注意到,此时您将失去对第三方令牌的所有控制权.这是特别危险的,因为大多数令牌可以访问各种数据,这打开了滥用的大门,最终以您的名义下降.最有可能的是,那些登录到您的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
Run Code Online (Sandbox Code Playgroud)

这很重要,因为在授权阶段之后,如果用户来自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
Run Code Online (Sandbox Code Playgroud)

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

使用本机SDK

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

这是因为您的服务器无法验证登录,并且被迫假设登录有效且真实,这意味着它绕过了Python Social Auth为您提供的所有安全性.

使用本机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
Run Code Online (Sandbox Code Playgroud)

您会注意到,这会在身份验证阶段跳过您的API,然后强制您的API对传入的令牌做出假设.但是肯定存在这种风险可能值得的情况,所以您应该先评估一下扔掉它.这是您的用户的快速登录和本机登录之间的权衡,并可能处理恶意或恶意令牌.


Fel*_* D. 9

我用你的A.选项解决了它.

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

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

这样,我可以发出像这样的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"
Run Code Online (Sandbox Code Playgroud)

我只是不确定我生成令牌的方式,这是一个好习惯吗?嗯,与此同时,它的作品!


Ser*_*ano 5

也许django-rest-framework-social-oauth2正是你要找的.这个软件包依赖于python-social-authdjango-oauth-toolkit,你已经在使用.我快速扫描了文档,似乎只是实现了你想要做的事情.