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

cerrina picture cerrina · Sep 24, 2012 · Viewed 8.1k times · Source

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. I'm using the following:

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

I can successfully authenticate users and access their blogs through the Google API at the time of authentication. When a user logs in, I store the access_token and refresh_token I receive from Google. and everything works great until the access_token expires. I'm trying to build the functionality that exchanges the refresh_token for a new access_token, but keep coming up against walls. 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. 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? returns false -- is there a different way I can check the expiration of the token besides using the value in the database?
  2. When I force the execution of client.authorization.fetch_access_token! I get an invalid_request error.

Can someone please let me know how I can exchange a refresh_token for a new access_token using the client API? 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. Thanks!!

Answer

brimil01 picture brimil01 · Sep 25, 2012

You may have already found this, but you can read through the whole process here at 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

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