Google-api-ruby-client: Use a refresh token to claim an access token?

Created on 15 Jan 2016  路  7Comments  路  Source: googleapis/google-api-ruby-client

Something that I would like to see in the README and if we can solve it here, I would gladly submit a pull request.

Given that a Web App has an expired access_token and a refresh_token, how does the client obtain a new access_token using that refresh token.

stored_user_credentials = {
refresh_token: 'TOKEN',
access_token: 'TOKEN',
expires_in: 3600,
issued_at: DateTime.parse('Fri, 15 Jan 2016 02:38:13 UTC +00:00')
}

client_secrets = GOOGLE_CALENDAR_CLIENT_SECRETS
client = Google::APIClient.new(application_name: GOOGLE_CALENDAR_APP)
calendar_api = client.discovered_api('calendar', 'v3')

client.authorization = client_secrets.to_authorization
    client.authorization.update!(
      scope: GOOGLE_CALENDAR_SCOPE,
      additional_parameters: {
        "access_type" => "offline",
        "include_granted_scopes" => "true"
      },
      redirect_uri: GOOGLE_CALLBACK_URL
    )

client.authorization.update_token!(stored_user_credentials)

#...

So far, I've not found an example of using the refresh token to obtain an access token anywhere. This setup step I've shown above works up until the access_token has expired and then no more. In my real app, stored_user_credentials is coming from an ActiveRecord model that caches these credentials.

triage me

Most helpful comment

The offending configuration seems to have been the

 additional_parameters: {
        "access_type" => "offline",
        "include_granted_scopes" => "true"
      },

Throwing a binding.pry to investigate, lead me to discover that calling client.authorization.refresh! directly was generating a message:
Signet::AuthorizationError: Authorization failed. Server message: { "error" : "invalid_request", "error_description" : "Parameter not allowed for this message type: access_type" }

Removing those additional parameters seems to have lead to the client exchanging a refresh token for a new access token as expected in this instant example.

All 7 comments

Generally speaking, you don't need to do much to exchange the refresh token. So long as you're properly serializing/deserializing the oauth2 client to restore the refresh token, all that needs to be done is call fetch_access_token!() to refresh. In this example, that would have to be after you call update_token!() to restore the refresh token.

That said, see https://github.com/google/google-auth-library-ruby for more current authorization code. It's a wrapper around signet. that makes things a bit easier. I'll put together a better sample at some point in the near future too.

The offending configuration seems to have been the

 additional_parameters: {
        "access_type" => "offline",
        "include_granted_scopes" => "true"
      },

Throwing a binding.pry to investigate, lead me to discover that calling client.authorization.refresh! directly was generating a message:
Signet::AuthorizationError: Authorization failed. Server message: { "error" : "invalid_request", "error_description" : "Parameter not allowed for this message type: access_type" }

Removing those additional parameters seems to have lead to the client exchanging a refresh token for a new access token as expected in this instant example.

Thank you @sqrrrl. I'll be sure to take a look.

Ah, smallish bug in signet there.

For that error I'd suggest moving where you specify additional_parameters. Instead of setting them as properties on the client, just pass them in as options to authorization_uri() so they're only used in that context (which is the case anyway, they're just used when building the initial authorization uri)

Can someone point me to an example of how I can get a refresh token working?

this is what I've got in my controller currently:

class Calendars::EventsController < ApplicationController
  def index
    if current_user
      client = ::Google::Apis::CalendarV3::CalendarService.new
      client.key = '...'
      client.authorization = {}
      client.authorization[:access_token] = current_user.oauth_token
      client.authorization[:refresh_token] = current_user.refresh_token if current_user.refresh_token
      @events = client.list_events(params[:calendar_id])
    end
  end
end

Added a small sinatra web app in the samples dir that hopefully helps.

@lifeiscontent did you ever figure that out?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gimite picture gimite  路  3Comments

gregology picture gregology  路  5Comments

pacuna picture pacuna  路  5Comments

martincalvert picture martincalvert  路  4Comments

jawadakram20 picture jawadakram20  路  6Comments