8
votes

Months ago I created a rails app that authenticated with google using oauth2 - specifically, the omniauth-google-oauth2 gem. I had all the steps working to create the authentication and store a refresh token, but recently oauth2 stopped sending back a 'refresh_token' as part of the response. Originally I was receiving a response that contained:

credentials: {
  refresh_token: XXX,
  token: YYY,
  expires_at: 1374840767,
  expires: true
},

Now I only get back the token that expires within an hour:

credentials: {
  token: YYY,
  expires_at: 1374840767,
  expires: true
},

I really don't know what I did on the application side to change this, so I'm not sure if something changed with google, or if it was something I did. For context, my code looks like:

initializers/omniauth.rb:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, 'KEY', 'SECRET', {:scope => "userinfo.email,userinfo.profile,analytics.readonly,adsense.readonly"}
end

authentications_controller.rb is where I receive the response:

def create
  auth = request.env["omniauth.auth"] 
  params = request.env["omniauth.params"]
  project = Project.find(params['project_id'])

  Authentication.create(:project_id => project.id, :provider => auth['provider'], :uid => auth['uid'], :access_token => auth['credentials']['refresh_token'])
  flash[:notice] = "Authentication successful."
  redirect_to owner_view_project_path(project)
end

Is it possible something is missing in my initializers/omniauth.rb file? I've tried adding the following to the options hash, but that didn't seem to bring back the refresh token:

:approval_prompt => "force", :access_type => "offline"

Any help would be greatly appreciated! Thanks in advance!

3
did you solve this problem? I/m facing the same problem now and I'd love to see you solution. Thanks! - Gustavo G.

3 Answers

10
votes

The prompt: 'consent' is getting you a refresh token. Something like this should work:

Rails.application.config.middleware.use OmniAuth::Builder do
  scopes = [
      # we need the profile scope in order to login
      "https://www.googleapis.com/auth/userinfo.profile",
      # this and other scopes could be added, but match them up with the
      # features you requested in your API Console
      "https://www.googleapis.com/auth/calendar"
    ]

  provider :google_oauth2, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, { scope: scopes.join(" "), access_type: 'offline',  prompt: 'consent'}
end
2
votes

As I see, it is enough to add

access_type: 'offline'

to your provider's scope.

The trick is as follows in the Google definition: the refresh token is supplied only in the first time that your app get access to the google account.

Deny authorization and regain authorization if you want to see, again, the refresh token beeing sent by google.

See google original definition here: "If your application needs to refresh access tokens when the user is not present at the browser, then use access_type: offline. This will result in your application obtaining a refresh token the first time your application exchanges an authorization code for a user." (https://developers.google.com/identity/protocols/OAuth2WebServer)

0
votes

It looks like you need to userinfo.email and userinfo.profile in your scope.

https://github.com/zquestz/omniauth-google-oauth2/issues/27