简体   繁体   English

Rails Google Client API - 无法为访问令牌交换刷新令牌

[英]Rails Google Client API - unable to exchange a refresh token for access token

After struggling with some SSL issues on my machine, I'm still trying to access a user's Blogger account through the Google Ruby Client API. 在我的机器上遇到一些SSL问题之后,我仍然试图通过Google Ruby Client API访问用户的Blogger帐户。 I'm using the following: 我正在使用以下内容:

  • Rails 3.2.3 Rails 3.2.3
  • Ruby 1.9.3 Ruby 1.9.3
  • oauth2 (0.8.0) oauth2(0.8.0)
  • omniauth (1.1.1) omn​​iauth(1.1.1)
  • omniauth-google-oauth2 (0.1.13) omn​​iauth-google-oauth2(0.1.13)
  • google-api-client (0.4.6) google-api-client(0.4.6)

I can successfully authenticate users and access their blogs through the Google API at the time of authentication. 我可以在身份验证时通过Google API成功验证用户身份并访问他们的博客。 When a user logs in, I store the access_token and refresh_token I receive from Google. 当用户登录时,我存储了我从Google收到的access_tokenrefresh_token and everything works great until the access_token expires. 并且在access_token到期之前一切正常。 I'm trying to build the functionality that exchanges the refresh_token for a new access_token , but keep coming up against walls. 我正在尝试构建为新的access_token交换refresh_token的功能,但不断遇到问题。 Using the client documentation as an example, this is the code I'm using: 客户端文档为例,这是我正在使用的代码:

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

This code works perfectly while the access_token is valid. access_token有效时,此代码可以正常工作。 As soon as it expires though, I'm seeing 2 problems: 一旦它到期,我就会看到两个问题:

  1. Even though I know the token is expired (I've checked expires_at value in the database), client.authorization.expired? 即使我知道令牌已过期(我已经检查了数据库中的expires_at值), client.authorization.expired? returns false -- is there a different way I can check the expiration of the token besides using the value in the database? 返回false - 除了使用数据库中的值之外,还有不同的方法可以检查令牌的过期吗?
  2. When I force the execution of client.authorization.fetch_access_token! 当我强制执行client.authorization.fetch_access_token! I get an invalid_request error. 我收到invalid_request错误。

Can someone please let me know how I can exchange a refresh_token for a new access_token using the client API? 有人可以告诉我如何使用客户端API为新的access_token交换refresh_token吗? Even if you know how to do it in another language, that would be a big help as I can then try to Rubyfy it. 即使你知道如何用另一种语言来做,这将是一个很大的帮助,因为我可以尝试Rubyfy它。 Thanks!! 谢谢!!

You may have already found this, but you can read through the whole process here at google: https://developers.google.com/accounts/docs/OAuth2WebServer 您可能已经发现了这一点,但您可以在google上阅读整个过程: https//developers.google.com/accounts/docs/OAuth2WebServer

The omniauth-google-oauth2 strategy already takes care of setting the access_type and approval_prompt so getting a refresh token is just a matter of posting to https://accounts.google.com/o/oauth2/token with grant_type=request_token omn​​iauth-google-oauth2策略已经负责设置access_type和approval_prompt,因此获取刷新令牌只需通过grant_type = request_token发布到https://accounts.google.com/o/oauth2/token

Here is roughly the code I use: 大致是我使用的代码:

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

Since you are using the Ruby Google API Client, why not use it to exchange the refresh token as well? 由于您使用的是Ruby Google API客户端,为什么不使用它来交换刷新令牌呢? The Ruby API does pretty much the same thing internally, which @brimil01 has said in his answer. Ruby API在内部做了几乎相同的事情,@ brimil01在他的回答中说过。

This is how I use the Ruby API to exchange my refresh token for a new access token. 这就是我使用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

And according to this issue here , it is recommended not to use the expired? 而根据这个问题 ,建议不要使用expired? method to check if an access token has expired. 检查访问令牌是否已过期的方法。

Basically, don't call the expired? 基本上,不要拨打过期? method. 方法。 There are essentially zero scenarios where that's a good idea. 基本上没有场景,这是一个好主意。 It simply won't give you reliable expiration information. 它根本不会给你可靠的到期信息。 It's more of a hint than a real expiration timestamp, and the token server may decide to honor an expired token anyways in certain somewhat theoretical, but important, circumstances. 它更多的是提示而不是真正的到期时间戳,并且令牌服务器可能决定在某些理论上但重要的情况下尊重过期的令牌。 If you do get an invalid grant error, always refresh your access token and retry once. 如果确实收到无效授权错误,请始终刷新访问令牌并重试一次。 If you're still getting an error, raise the error. 如果仍然出现错误,请提出错误。

Here is what I do. 这就是我的工作。

# 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

I fixed it with simple code below. 我用下面的简单代码修复了它。

   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 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM