2014-09-28 49 views
1

我使用django-all-access爲Facebook,Twitter和LinkedIn執行OAuth身份驗證。 Facebook和Twitter工作正常,LinkedIn正在將我重定向到錯誤的頁面。使用django-all-access對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/~

任何人都可以幫忙嗎? 最好。

回答

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。我聯繫了他,這是他的指導,讓我得出這些結論。

希望這可以幫助別人! 永別了。