【问题标题】:Authenticating with LinkedIn using django-all-access使用 django-all-access 通过 LinkedIn 进行身份验证
【发布时间】:2014-11-22 23:44:31
【问题描述】:

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

我的第一个猜测是我的应用设置在 LinkedIn 中配置不正确,所以这是我的设置:

OAuth 2.0 重定向网址:http://localhost:8000/accounts/callback/linkedin/,http://localhost:8000/accounts/profile/

OAuth 1.0 接受重定向 URL:http://localhost:8000/accounts/profile/

我的第二个猜测是profile_url 参数错误,即https://api.linkedin.com/v1/people/~

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

【问题讨论】:

    标签: python django oauth linkedin


    【解决方案1】:

    这有两个问题。首先,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。我联系了他,正是他的指导让我得出了这些结论。

    希望这对其他人也有帮助! 再见。

    【讨论】:

      猜你喜欢
      • 2014-05-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 2017-12-17
      • 1970-01-01
      • 2015-06-29
      相关资源
      最近更新 更多