15

在我的计算机上与SSL issues挣扎后,我仍然试图通过Google Ruby客户端API访问用户的Blogger帐户。我使用了以下内容:Rails Google客户端API - 无法为访问令牌交换刷新令牌

  • 的Rails 3.2.3
  • 的Ruby 1.9.3
  • 的oauth2(0.8.0)
  • omniauth(1.1.1)
  • omniauth - 谷歌 - 的oauth2 (0.1.13)
  • 谷歌的API客户端(0.4.6)

我能成功验证的用户,并通过谷歌访问一个自己的博客PI在认证时。当用户登录时,我会存储我从Google收到的access_tokenrefresh_token。并且一切都很好,直到access_token到期。我试图建立功能,交换refresh_token为新的access_token,但继续对墙壁。使用client documentation作为一个例子,这是我使用的代码:

client = Google::APIClient.new 
    token_pair = auth.oauth_token # access_token and refresh_token received during authentication 

    # Load the access token if it's available 
    if token_pair 
    client.authorization.update_token!(token_pair.to_hash) 
    end    

    # Update access token if expired 
    if client.authorization.refresh_token && client.authorization.expired? 
    client.authorization.fetch_access_token! 
    end 

    blogger = client.discovered_api('blogger', 'v3') 
    result = client.execute(
     api_method: blogger.blogs.list_by_user, 
     parameters: {'userId' => "self", 'fields' => 'items(description,id,name,url)'}, 
     headers: {'Content-Type' => 'application/json'}) 

此代码工作完美而access_token是有效的。只要它虽然到期,我看到两个问题:

  1. 尽管我知道令牌已过期(我已经在数据库中检查expires_at值),client.authorization.expired?回报false - 有一种不同的方式除了使用数据库中的值之外,我可以检查令牌的到期日期吗?
  2. 当我强制执行client.authorization.fetch_access_token!时,出现invalid_request错误。

有人可以让我知道如何使用客户端API将refresh_token换成access_token?即使你知道如何用另一种语言来做,这将是一个很大的帮助,因为我可以尝试Rubyfy它。谢谢!!

回答

25

您可能已经发现了这一点,但你可以在整个过程在这里谷歌阅读:https://developers.google.com/accounts/docs/OAuth2WebServer

的omniauth - 谷歌 - 的oauth2战略已采取设置ACCESS_TYPE的关怀和approval_prompt因此让刷新令牌刚刚张贴到https://accounts.google.com/o/oauth2/token与grant_type的事= request_token

下面是大致代码我使用:

def refresh_token 
    data = { 
    :client_id => GOOGLE_KEY, 
    :client_secret => GOOGLE_SECRET, 
    :refresh_token => REFRESH_TOKEN, 
    :grant_type => "refresh_token" 
    } 
    @response = ActiveSupport::JSON.decode(RestClient.post "https://accounts.google.com/o/oauth2/token", data) 
    if @response["access_token"].present? 
    # Save your token 
    else 
    # No Token 
    end 
rescue RestClient::BadRequest => e 
    # Bad request 
rescue 
    # Something else bad happened 
end 
+0

brimil01,你是男人!我甚至不知道为什么会这样,我的其他尝试进行HTTP调用没有,但我一直在看这个12个小时,所以我明天就会明白。你的代码完全可以工作,你已经从大规模的头撞中拯救了我,非常感谢。 – cerrina

+0

谢谢。一直试图弄清楚这一点。 –

+0

我发布这个时似乎得到了这个: #“invalid_request”},@response =# ,@headers = {“cache-control”=> [“no-cache,no-store,max-age = 0,must-revalidate”],“pragma”=> [“no-cache”],“expires” =“[”Fri,01 Jan 01 00:00:00 GMT“],”date“=> [”Wed,27 Mar 2013 08:50:48 GMT“],”content-type“=> [”application/json“],”x-content-type-options“=> [”nosniff“],”x-frame-options“=> [”SAMEORIGIN“],”x-xss-protection“=> [”1; mode ='block'],“server”=> [“GSE”],“connection”=> [“close”]}> ... –

16

由于您使用的是Ruby Google API客户端,为什么不使用它来交换刷新令牌呢? Ruby API在内部做了几乎相同的事情,@ brimil01在他的回答中已经说过。

这就是我如何使用Ruby API将我的刷新令牌交换为新的访问令牌。

def self.exchange_refresh_token(refresh_token) 
    client = Google::APIClient.new 
    client.authorization.client_id = CLIENT_ID 
    client.authorization.client_secret = CLIENT_SECRET 
    client.authorization.grant_type = 'refresh_token' 
    client.authorization.refresh_token = refresh_token 

    client.authorization.fetch_access_token! 
    client.authorization 
end 

而且根据this issue here,建议不要使用expired?方法来检查,如果一个访问令牌已过期。

基本上不要叫过期?方法。在这种情况下,这基本上是零的情况,这是一个不错的主意。它根本不会给你可靠的 到期信息。这更像是一个暗示,而不是实际到期 时间戳,并且令牌服务器无论如何可以决定在某些有些理论上但重要的情况下兑现过期令牌 。 如果您确实收到无效授权错误,请始终刷新您的访问令牌 并重试一次。如果您仍然收到错误,请提出错误。

这是我做的。

# Retrieved stored credentials for the provided user email address. 
# 
# @param [String] email_address 
# User's email address. 
# @return [Signet::OAuth2::Client] 
# Stored OAuth 2.0 credentials if found, nil otherwise. 
def self.get_stored_credentials(email_address) 
    hash = Thread.current['google_access_token'] 
    return nil if hash.blank? 

    hash[email_address] 
end 

## 
# Store OAuth 2.0 credentials in the application's database. 
# 
# @param [String] user_id 
# User's ID. 
# @param [Signet::OAuth2::Client] credentials 
# OAuth 2.0 credentials to store. 
def self.store_credentials(email_address, credentials) 
    Thread.current['google_access_token'] ||= {} 
    Thread.current['google_access_token'][email_address] = credentials 
end 


def self.credentials_expired?(credentials) 
    client = Google::APIClient.new 
    client.authorization = credentials 
    oauth2 = client.discovered_api('oauth2', 'v2') 
    result = client.execute!(:api_method => oauth2.userinfo.get) 

    (result.status != 200) 
end 


# @return [Signet::OAuth2::Client] 
# OAuth 2.0 credentials containing an access and refresh token. 
def self.get_credentials 
    email_address = '' 

    # Check if a valid access_token is already available. 
    credentials = get_stored_credentials(email_address) 
    # If not available, exchange the refresh_token to obtain a new access_token. 

    if credentials.blank? 
    credentials = exchange_refresh_token(REFRESH_TOKEN) 
    store_credentials(email_address, credentials) 
    else 
    are_credentials_expired = credentials_expired?(credentials) 

    if are_credentials_expired 
     credentials = exchange_refresh_token(REFRESH_TOKEN) 
     store_credentials(email_address, credentials) 
    end 
    end 

    credentials 
end 
+1

在'credentials_expired?'中,调用'client。 execute'不应该使用'execute!'形式,因为如果凭证过期而不是使用未授权状态填充'result',它将引发异常。 – pauljm

0

我用下面的简单代码修复了它。

def refesh_auth_tooken(refresh_token) 
     client = Google::APIClient.new 
     puts "REFESH TOOKEN" 
     client.authorization = client_secrets 
     client.authorization.refresh_token = refresh_token 

     #puts YAML::dump(client.authorization) 

     client.authorization.fetch_access_token! 
     return client.authorization 

    end 
相关问题