2014-03-05 50 views
0

我在GAE中以應用程序身份驗證Github API時遇到問題(GAE在使用Github3時拋出異常)。在GAE上使用Python對Github API進行身份驗證

import os, sys 
sys.path.append("lib") 
import jinja2, webapp2, urllib 

from google.appengine.api import users, oauth, urlfetch 

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), 
    extensions=['jinja2.ext.autoescape'], 
    autoescape=True) 

class ConsoleLogin(webapp2.RequestHandler): 

    def get(self): 
     google_user = users.get_current_user() 

     if google_user: 
      fields = { 
       "client_id" : os.environ.get('CLIENT_ID'), 
       "scope" : "user, repo" 
      } 
      url = 'https://github.com/login/oauth/authorize' 
      data = urllib.urlencode(fields) 
      result = urlfetch.fetch(url=url, 
       payload=data, 
       method=urlfetch.GET 
      ) 

在這段代碼之後,你應該從Github獲得一個臨時代碼。

問題:我根本找不到它。我在導遊裏看到你應該把它當作一個環境變量,但我看不到它。

幫助我完成Python腳本的人的額外要點。 ;)

+0

完全可以在GAE上使用請求。而github3似乎只需要作爲一個依賴,所以它應該是兼容的。 –

+0

但是令牌在'result'中,而不是環境變量。 –

+0

嗨@DanielRoseman,每次我嘗試使用請求時,都會拋出一個奇怪的異常。你能寫出代碼從結果中獲取令牌嗎?這將是一個巨大的幫助。 – Ben

回答

0

我不是說這是相當 - 這不是。這段代碼很醜陋,但它使用GAE,Webapp2和urllib2,而不是其他框架/庫。

import os, sys, cgi, json, cookielib 
sys.path.append("lib") 
import jinja2, webapp2, urllib, urllib2 

from google.appengine.api import users, oauth, urlfetch 
from webapp2_extras import sessions 

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), 
    extensions=['jinja2.ext.autoescape'], 
    autoescape=True) 

class BaseHandler(webapp2.RequestHandler): 
    def dispatch(self): 
     # Get a session store for this request. 
     self.session_store = sessions.get_store(request=self.request) 

     try: 
      # Dispatch the request. 
      webapp2.RequestHandler.dispatch(self) 
     finally: 
      # Save all sessions. 
      self.session_store.save_sessions(self.response) 

    @webapp2.cached_property 
    def session(self): 
     # Returns a session using the default cookie key. 
     return self.session_store.get_session() 

class ConsoleLogin(BaseHandler): 
    def get(self): 
     # Set variables to avoid problems later 
     code = '' 
     url = '' 
     access_token = '' 
     scope = '' 
     username = '' 

     google_user = users.get_current_user() 

     if google_user: 
      url = self.request.url 
      if ('code' not in url and not self.session.get('access_token')): 
       # First time user coming to site. Redirect to GH for code 
       url = 'https://github.com/login/oauth/authorize?scope=user,repo&client_id=' + os.environ.get('CLIENT_ID') 
       self.redirect(url) 
      elif 'code' in url: 
       # User has been to GH, continue with auth process 
       code = url.replace('http://localhost:8080/?code=', '') 

       # We have code, now get Access Token 
       fields = { 
        "client_id" : os.environ.get('CLIENT_ID'), 
        "client_secret" : os.environ.get('CLIENT_SECRET'), 
        "code" : code 
       } 
       url = 'https://github.com/login/oauth/access_token' 
       data = urllib.urlencode(fields) 
       result = urlfetch.fetch(url=url, 
        payload=data, 
        method=urlfetch.POST 
       ) 

       # Get the query string 
       query_string = str(result.content) 

       # Get the access token out of the full query string 
       access_token = query_string[13:] 
       end_access = access_token.find('&') 
       access_token = access_token[:end_access] 

       # Get the scope out of the full query string 
       start_scope = query_string.find('scope') 
       end_scope = query_string.find('token_type') 
       start_scope = start_scope + 6 # remove the word 'scope=' 
       end_scope = end_scope - 1  # remove the & symobol 
       scope = query_string[start_scope:end_scope] 
       scope = scope.split('%2C') 

      # Store the Access Token in a Session Variable 
      self.session['access_token'] = access_token 
      self.session['scope'] = scope 

      # And redirect to the base URL for neatness and to avoid other issues 
      self.redirect('/') 

      access_token = self.session.get('access_token') 
      scope = self.session.get('scope') 

      context = { 
       'access_token' : access_token, 
       'scope' : scope, 
       'username' : username, 
      } 

      # Template Settings 
      temp = 'templates/index.html' 

      template = JINJA_ENVIRONMENT.get_template(temp) 
      self.response.write(template.render(context)) 


config = {} 
config['webapp2_extras.sessions'] = { 
    'secret_key': 'the-beatles-will-always-rule', 
} 

application = webapp2.WSGIApplication([ 
    ('/', ConsoleLogin), 
], debug=True, config=config) 
0

當您運行您的OAuth授權請求時,您似乎缺少一些項目。按照GitHub API docs,你需要傳遞4個參數的授權請求(這是對的OAuth 2協議標準反正):

  • client_id(它來自你的GitHub的應用註冊 - 你確定它駐留在一個OS環境變量?你是否自己把它放在那裏?爲了測試的目的,你可以先把它作爲一個簡單的字符串放在代碼中;一旦一切正常,你會做得更好。
  • scope(你已經定義了它 - 這是行);
  • 一個隨機的state,你選擇,這將通過GitHub在下一步回傳給你;
  • 以及更重要的是一個redirect_uri GitHub會將客戶端轉發到用戶允許訪問他的帳戶後:它必須是您自己網站上的URL,您需要處理以獲取codestate參數

redirect_uri可能是 - 例如 - http://localhost:8080/oauth/accept_github,然後你需要準備好app.yaml文件和Python代碼來處理,以/oauth/accept_github提出了要求。在處理這些請求的代碼中,嘗試顯示以下內容:self.request.get('state')self.request.get('code'):如果一切正常,它們應該包含GitHub API發回給您的內容。現在您已準備好進行下一步:將您的code轉換爲access_token :)

2

以下是GitHub oAuth身份驗證的實際實現。它構建在Flask而不是Webapp2上,但可以輕鬆將處理程序移植到Webapp2。你可以看看gae引導程序項目gae-init,並且特定的片段是從容納各種oAuth的分支提供的gae-init-auth。 (注:裝飾@github.tokengetter由​​提供)

github_oauth = oauth.OAuth() 

github = github_oauth.remote_app(
    'github', 
    base_url='https://api.github.com/', 
    request_token_url=None, 
    access_token_url='https://github.com/login/oauth/access_token', 
    authorize_url='https://github.com/login/oauth/authorize', 
    consumer_key=config.CONFIG_DB.github_client_id, 
    consumer_secret=config.CONFIG_DB.github_client_secret, 
    request_token_params={'scope': 'user:email'}, 
) 


@app.route('/_s/callback/github/oauth-authorized/') 
@github.authorized_handler 
def github_authorized(resp): 
    if resp is None: 
    return 'Access denied: error=%s' % flask.request.args['error'] 
    flask.session['oauth_token'] = (resp['access_token'], '') 
    me = github.get('user') 
    user_db = retrieve_user_from_github(me.data) 
    return signin_user_db(user_db) 


@github.tokengetter 
def get_github_oauth_token(): 
    return flask.session.get('oauth_token') 


@app.route('/signin/github/') 
def signin_github(): 
    return github.authorize(
     callback=flask.url_for('github_authorized', 
      next=util.get_next_url(), 
      _external=True, 
     ) 
    ) 


def retrieve_user_from_github(response): 
    auth_id = 'github_%s' % str(response['id']) 
    user_db = model.User.retrieve_one_by('auth_ids', auth_id) 
    if user_db: 
    return user_db 
    return create_user_db(
     auth_id, 
     response['name'] or response['login'], 
     response['login'], 
     response['email'] or '', 
    ) 
+0

謝謝 - 你提到*「decorator' @ github.tokengetter'由'flask_oauth提供。py'「*,但它實際上並沒有被使用,請你提供'get_github_oauth_token()'函數/ tokengetter裝飾器的解釋嗎? – andyandy

相關問題