【问题标题】:Authenticating with the Github API using Python on GAE在 GAE 上使用 Python 使用 Github API 进行身份验证
【发布时间】:2014-03-05 17:20:52
【问题描述】:

我在将 Github API 作为 GAE 中的应用程序进行身份验证时遇到问题(当我使用 Github3 时,GAE 会引发异常)。

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 脚本的人都可以加分。 ;)

【问题讨论】:

  • 完全可以在GAE上使用request。而 github3 似乎只需要它作为依赖项,所以它也应该兼容。
  • 但是令牌在result,而不是环境变量中。
  • 嘿@DanielRoseman,每次我尝试使用请求时,都会抛出一个奇怪的异常。您能否写出代码以从结果中获取令牌?这将是一个巨大的帮助。

标签: python google-app-engine python-2.7 github-api


【解决方案1】:

这是 GitHub oAuth 身份验证的实际实现。它是基于 Flask 而不是 Webapp2 构建的,但您可以轻松地将处理程序移植到 Webapp2。您可以查看一个 gae bootstrap 项目 gae-init,特定的 sn-p 取自一个支持各种 oAuth 提供 gae-init-auth 的分支。 (注:装饰器@github.tokengetterflask_oauth.py提供)

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 '',
    )

【讨论】:

  • 谢谢 - 你提到 "decorator @github.tokengetterflask_oauth.py 提供",但它实际上似乎没有被使用。请您解释一下get_github_oauth_token() function/tokengetter 装饰器吗?
【解决方案2】:

运行 OAuth 授权请求时,您似乎遗漏了一些项目。根据GitHub API docs,您需要将4个参数传递给授权请求(无论如何这是OAuth 2协议的标准):

  • client_id(它来自您的 GitHub 应用程序注册 - 您确定它驻留在操作系统环境变量中吗?您自己把它放在那里吗?出于测试目的,您可以先将它作为纯字符串放入代码中;一旦一切正常,你以后会做得更好);
  • scope(您已经定义了它 - 没关系);
  • 您选择的随机state 将在下一步由 GitHub 回显给您;
  • ,更重要的是,一个redirect_uri,一旦用户允许访问他的帐户,GitHub 会将客户端转发到该地址:它必须是您自己网站上的一个 URL,您需要处理该 URL 才能同时获取 @987654326 @ 和state 参数

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 :)

【讨论】:

    【解决方案3】:

    我不是说它很漂亮——它不是。这段代码很丑陋,但它可以使用 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)
    

    【讨论】:

      猜你喜欢
      • 2013-08-03
      • 2015-09-10
      • 1970-01-01
      • 2014-05-28
      • 1970-01-01
      • 2015-02-17
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多