【问题标题】:Google+ API server side flow Rails 4.0 - Invalid CredentialsGoogle+ API 服务器端流程 Rails 4.0 - 凭据无效
【发布时间】:2014-01-04 13:56:31
【问题描述】:

我希望有更多经验的人可以帮助我了解这个 Google API。

我只是基于 ruby​​ 快速入门示例应用构建了一个演示应用,以探索这个 API。我有一个 Rails 4.0 应用程序并已成功(大部分)安装了 Google+ 登录。

一旦用户的访问令牌过期,一切都会出错。

我的测试应用的成功之处:

  1. 让用户登录并检索客户端的访问令牌以及服务器代码。
  2. 用服务器代码交换访问令牌和刷新令牌和 id 令牌
  3. 创建包含访问令牌和刷新令牌的令牌对对象,然后将其存储在会话哈希中
  4. 然后我的应用可以请求获取用户人员列表、插入时刻等。

所以我的问题是,使用刷新令牌获取新访问令牌的正确方法是什么?

使用下面的代码,访问令牌过期后我收到“无效凭据”的错误

如果我打电话给$client.authorization.refresh!,我会收到“无效请求”的错误

config/initializers/gplus.rb

# Build the global client
$credentials = Google::APIClient::ClientSecrets.load
$authorization = Signet::OAuth2::Client.new(
    :authorization_uri => $credentials.authorization_uri,
    :token_credential_uri => $credentials.token_credential_uri,
    :client_id => $credentials.client_id,
    :client_secret => $credentials.client_secret,
    :redirect_uri => $credentials.redirect_uris.first,
    :scope => 'https://www.googleapis.com/auth/plus.login',
    :request_visible_actions => 'http://schemas.google.com/AddActivity',
    :accesstype => 'offline')
$client = Google::APIClient.new(application_name: " App", application_version: "0.1")

*app/controllers/google_plus_controller.rb*

class GooglePlusController < ApplicationController

  respond_to :json, :js

  def callback

    if !session[:token]
      # Make sure that the state we set on the client matches the state sent
      # in the request to protect against request forgery.
      logger.info("user has no token")
      if session[:_csrf_token] == params[:state]
        # Upgrade the code into a token object.
        $authorization.code = request.body.read
        # exchange the one time code for an access_token, id_token and refresh_token from Google API server
        $authorization.fetch_access_token!
        # update the global server client with the new tokens
        $client.authorization = $authorization

        # Verify the issued token matches the user and client.
        oauth2 = $client.discovered_api('oauth2','v2')
        tokeninfo = JSON.parse($client.execute(oauth2.tokeninfo, 
          :access_token => $client.authorization.access_token, 
          :id_token => $client.authorization.id_token).response.body)

        # skipped

        token_pair = TokenPair.new
        token_pair.update_token!($client.authorization)
        session[:token] = token_pair
      else
        respond_with do |format|
          format.json { render json: {errors: ['The client state does not match the server state.']}, status: 401}
        end
      end # if session csrf token matches params token
      # render nothing: true, status: 200
    else
      logger.info("user HAS token")

    end # if no session token
    render nothing: true, status: 200
  end #connect


  def people
    # Check for stored credentials in the current user's session.
    if !session[:token]
      respond_with do |format|
        format.json { render json: {errors: ["User is not connected"]}, status: 401}
      end
    end
    # Authorize the client and construct a Google+ service 
    $client.authorization.update_token!(session[:token].to_hash)

    plus = $client.discovered_api('plus', 'v1')
    # Get the list of people as JSON and return it.
    response = $client.execute!(api_method: plus.people.list, parameters: {
        :collection => 'visible',
        :userId => 'me'}).body
    render json: response
  end

  #skipped
end

任何帮助表示赞赏。额外的问题,我用作指南的示例应用程序构建了一个全局授权对象(Signet::OAuth2::Client.new) - 但是我在过去一天阅读的其他文档已经声明为每个 API 请求构建一个授权对象。哪个是正确的?

【问题讨论】:

  • 能否提供 HTTP 层跟踪?请匿名 client_secret 和 user_ID。

标签: ruby-on-rails ruby google-plus google-oauth google-api-client


【解决方案1】:

这是我在应用中使用的片段:

require 'google/api_client'

        if client.authorization.expired? && client.authorization.refresh_token
            #Authorization Has Expired
            begin
                client.authorization.grant_type = 'refresh_token'
                token_hash = client.authorization.fetch_access_token!   
                goog_auth.access_token = token_hash['access_token']
                client.authorization.expires_in = goog_auth.expires_in || 3600
                client.authorization.issued_at = goog_auth.issued_at = Time.now
                goog_auth.save!
            rescue
                redirect_to user_omniauth_authorize_path(:google_oauth2)
            end

我正在使用 omniauth OAuth2 (https://github.com/intridea/omniauth-oauth2)、omniauth-google-oauth2 (https://github.com/zquestz/omniauth-google-oauth2) 和 google-api-ruby-client (https://code.google.com/p/google-api-ruby-client/)。

走过:

1) 如果访问令牌已过期并且我在数据库中保存了一个刷新令牌,那么我尝试刷新访问令牌。

2) 我将授权类型设置为“refresh_token”,然后设置为“fetch_access_token!”调用返回一个普通的旧哈希。

3) 我使用“access_token”键返回新的有效访问令牌。其余的键/值几乎可以忽略。

4) 我将“expires_in”属性设置为 3600(1 小时),将“issued_at”设置为 Time.now 并保存。

注意:确保首先设置 expires_in 和第二个 issue_at。底层的 Signet OAuth2 客户端将您的 OAuth2 客户端的 issue_at 值重置为 Time.now,而不是您从数据库中设置的值,您会发现所有对“过期?”的调用返回假。我将在底部发布 Signet 源代码。

5) 如果访问代码已过期并且我没有刷新令牌,我会重定向到 omniauth 路径,并从头开始整个过程​​,您已经概述了这一点。

注意:我将几乎所有内容都保存在数据库中:access_token、refresh_token,甚至是 auth_code。我使用 figaro gem 来保存特定于环境的值,例如 client_id、client_secret、oauth2_redirect 等。如果您使用多个 env 进行开发,请使用 figaro (https://github.com/laserlemon/figaro)。

这里是 Signet 源代码,它显示了手动设置 expires_in 是如何实际将 issue_at 重置为 Time.now(!)。因此,您需要先设置“expires_in”,然后使用您从数据库中获得的issued_at 值设置“issued_at”。设置 expires_in 秒实际上会将您的发出时间重置为 Time.now ... 例如;来电“过期?”将始终返回 false!

我怎么知道这个?正如 T 先生所说,“痛苦”。

http://signet.rubyforge.org/api/Signet/OAuth2/Client.html#issued_at%3D-instance_method

# File 'lib/signet/oauth_2/client.rb', line 555
def expires_in=(new_expires_in)
if new_expires_in != nil
@expires_in = new_expires_in.to_i
@issued_at = Time.now
else
@expires_in, @issued_at = nil, nil

结束

【讨论】:

  • 谢谢。感谢您的帮助。
【解决方案2】:

这行说: accesstype => '离线'

应该说: access_type => '离线'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-19
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多