我正在使用django-all-access为Facebook,Twitter和LinkedIn实施OAuth身份验证。 Facebook和Twitter工作正常,LinkedIn正在将我重定向到错误的页面。
这是我的设置(消费者密钥和秘密显然是混淆的):
[
{
"pk": null,
"model": "allaccess.provider",
"fields": {
"name": "facebook",
"consumer_key": "xxx",
"consumer_secret": "xxx",
"authorization_url": "https://www.facebook.com/dialog/oauth",
"access_token_url": "https://graph.facebook.com/oauth/access_token",
"request_token_url": "",
"profile_url": "https://graph.facebook.com/me"
}
},
{
"pk": null,
"model": "allaccess.provider",
"fields": {
"name": "twitter",
"consumer_key": "xxx",
"consumer_secret": "xxx",
"authorization_url": "https://api.twitter.com/oauth/authenticate",
"access_token_url": "https://api.twitter.com/oauth/access_token",
"request_token_url": "https://api.twitter.com/oauth/request_token",
"profile_url": "https://api.twitter.com/1.1/account/verify_credentials.json"
}
},
{
"pk": null,
"model": "allaccess.provider",
"fields": {
"name": "linkedin",
"consumer_key": "xxx",
"consumer_secret": "xxx",
"authorization_url": "https://www.linkedin.com/uas/oauth2/authorization",
"access_token_url": "https://www.linkedin.com/uas/oauth2/accessToken",
"request_token_url": "",
"profile_url": "https://api.linkedin.com/v1/people/~"
}
}
]
Facebook和Twitter都在使用正确的身份验证流程并正确注册用户,但Twitter会将我重定向到错误的页面,而根本不会注册用户。这是LinkedIn流程(我删除了大多数参数,并离开redirect_uri
):
https://www.linkedin.com/uas/oauth2/authorization?redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Faccounts%2Fcallback%2Flinkedin%2F&response_type=code
http://localhost:8000/accounts/callback/linkedin/
http://localhost:8000/accounts/login/
我的第一个猜测是我的应用设置在LinkedIn中配置不正确,所以这是我的设置:
OAuth 2.0重定向网址:http://localhost:8000/accounts/callback/linkedin/,http://localhost:8000/accounts/profile/
OAuth 1.0接受重定向网址:http://localhost:8000/accounts/profile/
我的第二个猜测是profile_url
参数错误,即https://api.linkedin.com/v1/people/~
。
有人可以帮忙吗? 最好的。
答案 0 :(得分:3)
这有两个问题。首先,LinkedIn希望access_token
参数名为oauth2_access_token
,不符合RFC 6750。此外,LinkedIn默认情况下不会返回JSON,这是allaccess客户端所期望的。因此,您还需要在调用中添加format=json
作为参数。
这主要是通过自定义OAuth2Client.request
方法来实现的,但在我的情况下,我更进了一步。 allaccess框架将访问令牌作为查询参数发送,这通常是不鼓励的,因为令牌随后会记录在服务器上,这可能不安全。相反,OAuth 1和2都支持在Authorization
请求标头中发送令牌。 OAuth 1 is a bit more complicated,OAuth 2 requires only a bearer token。
因此,我定制了OAuth2Client
类来处理这两种情况。
from allaccess.clients import OAuth2Client as _OAuth2Client
from requests.api import request
class OAuth2Client(_OAuth2Client):
def request(self, method, url, **kwargs):
user_token = kwargs.pop('token', self.token)
token, _ = self.parse_raw_token(user_token)
if token is not None:
# Replace the parent method so the token is sent on the headers. This is
# safer than using query parameters, which is what allaccess does
headers = kwargs.get('headers', {})
headers['Authorization'] = self.get_authorization_header(token)
kwargs['headers'] = headers
return request(method, url, **kwargs)
def get_authorization_header(self, token):
return 'Bearer %s' % (token,)
class OAuth2LinkedInClient(OAuth2Client):
def request(self, method, url, **kwargs):
# LinkedIn does not return JSON by default
params = kwargs.get('params', {})
params['format'] = 'json'
kwargs['params'] = params
return super(OAuth2LinkedInClient, self).request(method, url, **kwargs)
OAuth2Client
现在在请求标头中发送访问令牌而不是查询参数。此外,LinkedIn客户端添加format
查询参数并将其设置为json
。由于已经在标头中发送了令牌,因此无需替换OAuth 1身份验证。
不幸的是,这不是整个交易。我们现在需要让allaccess知道使用这些客户端,我们通过自定义视图来实现。这是我的实施:
from allaccess.views import OAuthRedirect as _OAuthRedirect
from allaccess.views import OAuthCallback as _OAuthCallback
from allaccess.views import OAuthClientMixin as _OAuthClientMixin
from django.core.urlresolvers import reverse
from authy.clients import OAuth2Client, OAuth2LinkedInClient
class OAuthClientMixin(_OAuthClientMixin):
def get_client(self, provider):
# LinkedIn is... Special
if provider.name == 'linkedin':
return OAuth2LinkedInClient(provider)
# OAuth 2.0 providers
if not provider.request_token_url:
return OAuth2Client(provider)
# Let allaccess chose other providers (those will be mostly OAuth 1)
return super(OAuthClientMixin, self).get_client(provider)
class OAuthRedirect(OAuthClientMixin, _OAuthRedirect):
# This is necessary because we'll be setting these on our URLs, we can no longer
# use allaccess' URLs.
def get_callback_url(self, provider):
return reverse('authy-callback', kwargs={ 'provider': provider.name })
class OAuthCallback(OAuthClientMixin, _OAuthCallback):
# We need this. Notice that it inherits from our own client mixin
pass
现在将URL设置为映射到我们自己的实现:
from django.conf.urls import url
from .views import OAuthRedirect, OAuthCallback
urlpatterns = [
url(r'^login/(?P<provider>(\w|-)+)/$', OAuthRedirect.as_view(), name='authy-login'),
url(r'^callback/(?P<provider>(\w|-)+)/$', OAuthCallback.as_view(), name='authy-callback'),
]
然而,还有一个问题尚未解决。问题是其他类也使用客户端。例如,我可以找到allaccess.models.AccountAccess.api_client
方法。我不确定是否还有更多。现在的问题是我们的视图可能正在使用我们的客户端,而其他类正在使用不同的客户端。我不确定这可能是一个什么问题,但是因为它没有咬我,现在我继续使用这段代码。
最后,我要感谢并感谢allaccess框架的创建者Mark Lavin。我联系了他,这是他的指导,引导我得出这些结论。
希望这对其他人也有帮助! 告别。