Skip to content

Instantly share code, notes, and snippets.

@GregBaugues
Last active July 11, 2022 02:21
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save GregBaugues/276d7c13e9f7a28db010 to your computer and use it in GitHub Desktop.
Save GregBaugues/276d7c13e9f7a28db010 to your computer and use it in GitHub Desktop.
Google API OAuth 2.0 refresh token (Ruby on Rails)
# The OAuth access token provided by the Google API expires in 60 minutes. After expiration,
# you must exchange a refresh token for a new access token. Unfortunately, the the Google API
# ruby gem does not include a method for refreshing access tokens.
# You can read up on how to refresh an access token here:
# https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
# This Token model implements that process. It's based off of a Token model that can be created
# by running:
# rails g model Token token:text refresh_token:string expires_at:datetime
# This code assumes you Google API CLIENT_ID and CLIENT_SECRET are set as environment variables.
# Google only sends the refresh token the first time you authorize your account.
# If you didn't save it, go to your Gmail Account Permissions and revoke permissions to your app
class Token < ActiveRecord::Base
def to_params
{ 'refresh_token' => refresh_token,
'client_id' => ENV['CLIENT_ID'],
'client_secret' => ENV['CLIENT_SECRET'],
'grant_type' => 'refresh_token'}
end
def request_token_from_google
url = URI("https://accounts.google.com/o/oauth2/token")
Net::HTTP.post_form(url, self.to_params)
end
def refresh!
data = JSON.parse(request_token_from_google.body)
update_attributes(
token: data['access_token'],
expires_at: Time.now + data['expires_in'].to_i.seconds
)
end
def self.access_token
#convenience method to retrieve the latest token and refresh if necessary
t = Token.last
t.refresh! if t.expires_at < Time.now
t.token
end
end
@emcmanus
Copy link

emcmanus commented Oct 3, 2021

Net::HTTP does not raise exceptions on HTTP failures (it just returns one of like 40 different error classes).

So when Google returns an error, which it occasionally does, response.body will be something like "{\n \"error\": \"\",\n \"error_description\": \"\"\n}".

This is valid JSON, so it will parse and evaluate to update_attributes(access_token: nil, expires_at: Time.now). Even if your refresh token is valid.

This is probably not what you want, so use a sane library, like RestClient, which will raise an exception instead:

  def request_token_from_google
    RestClient.post("https://accounts.google.com/o/oauth2/token", self.to_params)
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment