我正在使用Django Rest框架构建一个API。后来,这个API应该由iOS和Android设备使用。我想让我的用户注册OAuth2提供商,如Facebook和Google。在这种情况下,他们根本不必在我的平台上创建一个帐户。但是当没有Facebook/Google帐户时,用户也应该能够注册,我使用django-oauth工具包,所以我有自己的OAuth2提供程序。
对于外部提供者,我使用python,它工作良好,并自动创建用户对象。
我希望客户端通过使用承载令牌进行身份验证,这对于与我的提供者注册的用户( Django -oauth-toolkit提供了Django REST Framework的身份验证方案和权限类)很好。
但是,python只实现基于会话的身份验证,因此没有直接的方法代表外部oauth2提供程序注册的用户发出经过身份验证的API请求。
如果我使用django-oauth-工具箱生成的access_token,那么这样的请求是可行的:
curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/但是,由于Django REST框架没有相应的身份验证方案,并且python auth提供的AUTHENTICATION_BACKENDS只适用于基于会话的身份验证,所以以下内容无法工作:
curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/使用Django REST Framework提供的可浏览API (在使用python-social-auth进行身份验证后)工作得很好,只有没有会话cookie的API调用才不能工作。
我想知道解决这个问题的最佳方法是什么。在我看来,我基本上有两个选择:
答:当用户与外部oauth2提供程序(由python 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 auth。但是,由于API调用不能利用Django会话,这意味着我必须为Django Rest框架编写一个身份验证方案,该方案使用python存储的数据。有关如何做到这一点的一些提示可以在这里找到:
Cases.html#签名-通过-oauth-access-令牌
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只在登录时验证令牌,然后依赖Django会话。这意味着我必须找到一种方法来防止为每个无状态API请求执行整个OAuth2流,而是对照DB中存储的数据进行检查,因为它是以JSON的形式存储的,因此不是真正优化的数据(不过,我可以使用UserSocialAuth.objects.get(extra_data__contains=) )。
我还必须检查访问令牌的作用域,并使用它们来检查权限,这是django-oauth-toolkit已经做的事情(TokenHasScope、required_scopes等)。
目前,我倾向于使用选项A,因为Django -oauth-工具箱提供了与Django Rest框架很好的集成,并且我从盒子里得到了我需要的一切。唯一的缺点是,我必须将python auth检索的access_tokens“注入”到django-oauth-工具箱的AccessToken模型中,这感觉有些不对劲,但可能是目前为止最简单的方法。
是否有人反对这样做,或可能以不同的方式处理同样的问题?我是不是错过了一些显而易见的事情,让我的生活变得比必要的更艰难?如果有人已经将django-oauth工具包与python-social-auth和外部oauth2提供程序集成,我将非常感谢一些指点或意见。
发布于 2014-11-20 23:49:15
实现OAuth的许多困难归结于理解授权流应该如何工作。这主要是因为这是登录的“起点”,当使用第三方后端(使用类似于的工具)时,您实际上要这样做两次:一次用于API,一次用于第三方API。
使用API和第三方后端授权请求。
您需要通过的身份验证过程是:

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“作为第三方后端,但是这个过程对于任何后端都是一样的。
从您的移动应用程序的角度来看,/authorize只重定向到Django OAuth Toolkit提供的OAuth url。从那里开始,移动应用程序等待回调url到达,就像在标准的OAuth授权流中一样。几乎所有其他(Django登录,社交登录等)在后台由Django OAuth工具包或处理。
这也将与您使用的几乎所有OAuth库兼容,而且无论使用什么第三方后端,授权流都将同样工作。它甚至可以处理(常见的)情况,在这种情况下,您需要能够支持Django的身份验证后端(电子邮件/用户名和密码)以及第三方登录。

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客户端和用户的社会帐户之间的中介。

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发出请求。

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工具包(或OAuth提供程序)进行的。您所看到的只有一个与您的请求相关联的User 。

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处理所有内容。但是一个值得注意的例外是当使用本机SDK时,因为身份验证和授权是通过本机系统处理的,这意味着完全绕过了API 。这对于需要与第三方登录的应用程序或完全不使用您的API的应用程序来说是很好的,但是--当两者结合在一起时,这是一场噩梦。
这是因为您的服务器无法验证登录并被迫使用假设登录是有效的和真正的,这意味着它绕过了Python提供的任何和所有安全性。

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对传入的令牌进行假设。但是,在某些情况下,这种风险可能值得,因此您应该在抛出它之前对其进行评估。这是用户、和快速登录和本机登录之间的一种权衡,可能会处理坏的或恶意的令牌。
发布于 2015-04-05 18:31:30
我用你的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"我只是不确定我生成令牌的方式,这是一个很好的实践吗?嗯,同时,它是有效的!!
发布于 2015-04-13 15:22:23
也许你要找的就是django-rest-框架-社会-OAuth2。此包依赖于您已经使用的python-social-auth和django-oauth-toolkit。我很快地浏览了文档,它似乎实现了您想要做的事情。
https://stackoverflow.com/questions/27051209
复制相似问题