谷歌应用引擎 oauth2 提供商

Posted

技术标签:

【中文标题】谷歌应用引擎 oauth2 提供商【英文标题】:google app engine oauth2 provider 【发布时间】:2011-12-10 06:28:24 【问题描述】:

我想设置一个带有 oauth 2.0 提供程序的 rest api 进行身份验证。我使用 python。 是否有任何库用于设置在应用程序引擎上运行的用 python 编码的 oauth 2.0 提供程序? 谢谢。

【问题讨论】:

【参考方案1】:

你查看OAuth for Python的文章信息了吗?它说它是为了“此参考描述了如何将 OAuth 与 Python 应用程序一起用作服务提供者。”

【讨论】:

使用 google 进行身份验证而不是我的服务,这使得我的身份验证完全依赖于 google! 然后查看python-oauth2,了解 Python 中的完整 OAuth 实现。 我认为尽管有这个名字,但它只能用于创建 oauth 1.0 提供程序【参考方案2】:

Python 和 Java App Engine 运行时都内置了 OAuth2 支持。

在 Python 中,您只需要:

from google.appengine.api import oauth

# Note, unlike in the android app below, there's no 'oauth2:' prefix here
SCOPE = 'https://www.googleapis.com/auth/userinfo.email'

# magic happens here
user = oauth.get_current_user(SCOPE)

在 Java 中你会使用:

OAuthService oauth = OAuthServiceFactory.getOAuthService();

// Note, unlike in the Android app below, there's no 'oauth2:' prefix here
String SCOPE = "https://www.googleapis.com/auth/userinfo.email";

// magic happens here
User user = oauth.getCurrentUser(SCOPE);

这是完整的 Python 2.7 处理程序,可让您验证用户:

from google.appengine.api import oauth
import logging
import traceback
import webapp2


class MainHandler(webapp2.RequestHandler):

  def post(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.write('Hi there!\n')

    # Note, unlike in the Android app below, there's no 'oauth2:' prefix here
    scope = 'https://www.googleapis.com/auth/userinfo.email'
    try:
      self.response.write('\noauth.get_current_user(%s)' % repr(scope))

      # validates audience of the OAuth2 access token
      allowed_clients = ['407408718192.apps.googleusercontent.com'] # list your client ids here
      token_audience = oauth.get_client_id(scope)
      if token_audience not in allowed_clients:
        raise oauth.OAuthRequestError('audience of token \'%s\' is not in allowed list (%s)' % (token_audience, allowed_clients))          

      # gets user object for the user represented by the oauth token
      user = oauth.get_current_user(scope)
      self.response.write(' = %s\n' % user)
      self.response.write('- auth_domain = %s\n' % user.auth_domain())
      self.response.write('- email       = %s\n' % user.email())
      self.response.write('- nickname    = %s\n' % user.nickname())
      self.response.write('- user_id     = %s\n' % user.user_id())
    except oauth.OAuthRequestError, e:
      self.response.set_status(401)
      self.response.write(' -> %s %s\n' % (e.__class__.__name__, e.message))
      logging.warn(traceback.format_exc())


app = webapp2.WSGIApplication([
  ('/.*', MainHandler)
], debug=True)

app.yaml 很简单

application: your-app-id
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: main.app

请注意,客户端应在Authorization: Bearer HTTP 请求标头中发送 OAuth2 令牌,例如

Authorization: Bearer ya29XAHES6ZT4w72FecXjZu4ZWskTSX3x3OqYxUSTIrA2IfxDDPpI

如果您碰巧正在构建一个 Android 应用,您可以使用AccountManager 接口轻松生成这些令牌:

AccountManager accountManager = AccountManager.get(this);
Account[] accounts = accountManager.getAccountsByType("com.google");

// TODO: Allow the user to specify which account to authenticate with
for (Account account : accounts) 
  Log.i(TAG, "- account.name = " + account.name);


// Note the "oauth2:" prefix here
String authTokenType = "oauth2:https://www.googleapis.com/auth/userinfo.email";

// Note: AccountManager will cache these token, even after they've expired.
// TODO: Invalidate expired tokens, either after auth fails, or preemptively via:
// accountManager.invalidateAuthToken(accounts[0].type, token);

accountManager.getAuthToken(accounts[0], authTokenType, null, this,
    new AccountManagerCallback<Bundle>() 
      @Override
      public void run(AccountManagerFuture<Bundle> future) 
        try 
          String token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
          Log.i(TAG, "Got KEY_AUTHTOKEN: " + token);
          // Don't forget HTTP Header "Authorization: Bearer <token>"
          callAppEngineRestApi(token); // <---- Your code here
         catch (OperationCanceledException e) 
          Log.i(TAG, "The user has denied you access to the API");
         catch (Exception e) 
          Log.i(TAG, "Exception: ", e);
        
      
    , null);

如果您想查看所有内容,请随时查看这些项目以获取完整源代码:

https://github.com/fredsa/sauer.motivate-android安卓客户端应用 https://github.com/fredsa/sauer.motivate-appengine Python 2.7 App Engine 应用 https://github.com/fredsa/sauer.echo-headersJava App Engine 应用

【讨论】:

你确定吗? Afaik Gae 仅支持 Oauth 1.0a 我对你的代码进行了测试,但它不起作用 - 异常: - 我使用的是 python 2.5。 在 Google 和 *** 周围看了很多。这是我找到的唯一具有良好工作代码的答案。 (有关完整示例,请参阅提供的 URL)。也适用于 Java,因此问题标题可能会误导 Java 用户。 太棒了。我花了数周时间尝试在带有 python 的 appengine 上使用 OAuth 2.0,这似乎是唯一一篇谈论 oauth.get_current_user(scope) 的文章。它甚至不在文档上,但对我来说,这就是魅力!【参考方案3】:

我无法对上述答案发表评论,所以我在这里为任何与这个 sn-p 苦苦挣扎的人添加了它:

# magic happens here
user = oauth.get_current_user(SCOPE)

如果您使用服务帐户(从今天开始,我认为还有 Google 用户令牌),这已在 AppEngine 上被破坏了一个月,因为令牌长度会导致 AE 库出现问题。谷歌告诉我他们不太可能很快修复它。

这是目前唯一对我有用的东西:

    token = self.request.headers['Authorization'].split(' ')[1]
    url = 'https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=' + token
    oauth_response = urlfetch.fetch(url)
    if oauth_response.status_code != 200:
        raise Exception('Unable to authorise: /'.format(oauth_response.status_code, oauth_response.content))
    token_response = json.loads(oauth_response.content)
    email = token_response['email']

【讨论】:

这是做什么的?其背后的逻辑是什么? 它使用 google rest api 来“解码”承载令牌,从响应中提取电子邮件地址。

以上是关于谷歌应用引擎 oauth2 提供商的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security OAuth2:如何为两类用户提供两个单独的登录链接?

使用 RESTify Node.js 创建 OAuth2.0 服务提供者

克隆存储库时与谷歌应用引擎一起使用啥凭据?

Django OAuth2 错误:提供了 client_id 时,invalid_client 和 client_id=None

如何在谷歌应用引擎上将帖子从数据库异步加载到 django 模板?

Rest,Spring 拥有 OAuth2 服务器 + OAuth2 提供商,如 Facebook、Google、Yahoo