googleapis / signet

Signet is an OAuth 1.0 / OAuth 2.0 implementation.
Apache License 2.0
373 stars 164 forks source link

Refresh access token using long lived refresh token #131

Open rohitbegani opened 5 years ago

rohitbegani commented 5 years ago

I am trying to figure out how to update the access token using the refresh token which I got during the initial authorization. I tried multiple ways to do this but I always receive:

Signet::AuthorizationError: Authorization failed.  Server message:
{
  "error": "invalid_grant",
  "error_description": "Bad Request"
}

The main method to which I have been adding changes to is:

def refresh_google_calendar
  @service = Google::Apis::CalendarV3::CalendarService.new
  @service.authorization = google_secret

  # Attempts to refresh access token if already expired.
  @service.authorization.refresh! if @user.google_expires_at < Time.current
end

def google_secret
  Signet::OAuth2::Client.new(refresh_options)
end

def refresh_options
    { token_credential_uri: 'https://oauth2.googleapis.com/token',
      access_token: @user.google_token,
      expires_at: @user.google_expires_at,
      refresh_token: @user.google_refresh_token,
      client_id: ENV.fetch('GOOGLE_CLIENT_ID'),
      client_secret: ENV.fetch('GOOGLE_CLIENT_SECRET') }
  end

I tried another variation of the refresh_options as:

def refresh_options
  { refresh_token: @user.google_refresh_token,
      token_credential_uri: 'https://oauth2.googleapis.com/token',
      client_id: ENV.fetch('GOOGLE_CLIENT_ID'),
      client_secret: ENV.fetch('GOOGLE_CLIENT_SECRET') }
end

Also attempted using Google::Auth::UserRefreshCredentials as explained here but I still get the invalid_grant error.

I do know that my Client ID and Client Secret work because I use the same for the initial authorization request.

I would appreciate any help in figuring this out.

@sqrrrl @blowmage

gbxl commented 5 years ago

I have fought for the last 2 days with authentication and had the exact same issue. It now moved to being Faraday::ConnectionFailed => execution expired after I changed some code around. It seems I can't fetch_access_token anymore. And of course nor explicit error to help steering towards a solution : /

illustrarium commented 5 years ago

Hi guys, I have the same problem Last 2 weeks, did you find any solution for it?

I try to write some updates in Google Spreadsheets but always got: Faraday::ConnectionFailed wrapped=#<Net::OpenTimeout: execution expired

Signet::AuthorizationError: Unexpected error: #<Faraday::ConnectionFailed wrapped=#<Net::OpenTimeout: execution expired>>
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/googleauth-0.9.0/lib/googleauth/signet.rb:116:in 'rescue in retry_with_error'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/googleauth-0.9.0/lib/googleauth/signet.rb:105:in 'retry_with_error'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/googleauth-0.9.0/lib/googleauth/signet.rb:79:in 'fetch_access_token!'
  from /home/deploy/s/releases/20190923113936/app/services/googlespreadsheets.rb:54:in 'append'
  from (irb):2
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/railties-5.0.6/lib/rails/commands/console.rb:65:in 'start'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/railties-5.0.6/lib/rails/commands/console_helper.rb:9:in 'start'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:78:in 'console'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:49:in 'run_command!'
  from /home/deploy/s/shared/bundle/ruby/2.4.0/gems/railties-5.0.6/lib/rails/commands.rb:18:in '<top (required)>'
  from bin/rails:9:in 'require'
  from bin/rails:9:in '<main>'
jdugarte commented 4 years ago

I encounter the same problem (response message) with code not too dissimilar from this. Anyone has figure something out?

la-ruby commented 4 years ago

I was getting Signet::AuthorizationError: Authorization failed too a few days ago. As I understand it, access tokens are generated using refresh tokens. the refresh token is sent to us only the very first time a user authorizes your app. in my case, my application didn't properly persist the refresh token, so I went to this screen: image did an oauth disconnect/revoke, then the next time you go through the oauth dance you will get a refresh token and using that hopefully you can generate access tokens every 12 or so hours...

dariodaich commented 2 years ago

Assign access and refresh token values to your oauth client, and then call #refresh! on the oauth client.

Something like...

  oauth_client = Signet::OAuth2::Client.new(...)

  oauth_client.access_token = persisted_access_token
  oauth_client.refresh_token = persisted_refresh_token

  oauth_client.refresh!

#refresh! will get you a new access token which you can use to comm with the targeted service. It just wraps the #fetch_access_token!. image