使用django-all-access通过LinkedIn进行身份验证

时间:2014-09-28 13:32:51

标签: python django oauth linkedin

我正在使用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):

  1. https://www.linkedin.com/uas/oauth2/authorization?redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Faccounts%2Fcallback%2Flinkedin%2F&response_type=code
  2. http://localhost:8000/accounts/callback/linkedin/
  3. http://localhost:8000/accounts/login/
  4. 我的第一个猜测是我的应用设置在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/~

    有人可以帮忙吗? 最好的。

1 个答案:

答案 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 complicatedOAuth 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。我联系了他,这是他的指导,引导我得出这些结论。

希望这对其他人也有帮助! 告别。