zquestz / omniauth-google-oauth2

Oauth2 strategy for Google
1.44k stars 412 forks source link

Can't Access Refresh Token / Can't Bypass Refresh Flow #454

Open braintrustalex opened 4 months ago

braintrustalex commented 4 months ago

TLDR: I've used the setup guide to get Google working as an SSO provider, up until my access_token expires. App throws A refresh_token is not available on subsequent POST to /auth/google_oauth2. Not using Devise. How can I mitigate this error, or bypass the refresh_token flow?

Setup

I've followed the setup guide in the README, and I am not using Devise.

I am able to POST /auth/google_oauth2 with a user and complete the account selection and consent screens, and am redirected to my callback endpoint with an access_token and refresh_token. However, once the access_token expires and my user is logged out of my application, when they POST /auth/google_oauth2 again to re-authenticate, I hit the token refresh flow within omniauth-oauth2: https://github.com/omniauth/omniauth-oauth2/blob/3a43234ab5dd36a75f9c125c58fcfe1a37b26805/lib/omniauth/strategies/oauth2.rb#L92

This fails every time with A refresh_token is not available https://gitlab.com/oauth-xx/oauth2/-/blob/main/lib/oauth2/access_token.rb?ref_type=heads#L118

This makes sense, since the refresh_token is supplied only on the first authorization call to Google. However, what I don't understand is how I'm supposed to handle this.

I can only make this work if I edit the gemfiles and add to the existing code:

# control how quickly my access_token expires for testing purposes
fresh[:expires_in] = 1
# the actual fix: manually forcing my refresh token into the gem flow
fresh[:refresh_token] = "my valid refresh_token"

Configuration

Gemfile:

# sso multiprovider
gem 'omniauth'
gem "omniauth-rails_csrf_protection"
gem 'omniauth-google-oauth2'

config/initializers/omniauth.rb:

Rails.application.config.middleware.use OmniAuth::Builder do
  if [feature flag enabled]
    provider :google_oauth2, 
      [google client id], 
      [google client secret], 
      scope: 'email',
      skip_jwt: true,
      access_type: 'online'
  end
end

OmniAuth.config.allowed_request_methods = %i[post]
OmniAuth.config.logger = Rails.logger

routes.rb

get '/auth/google_oauth2/callback', to: 'my_controller#google_callback'

index.html.erb:

= button_to 'Login with Google', '/auth/google_oauth2', method: :post

Questions

So to my questions:

  1. The guides mention to store the refresh_token, but not how or where. How am I supposed to store and pass the refresh_token to this gem?
  2. Can I bypass the refresh flow entirely? I'm only using this gem to authenticate users, and not to access any other Google API. Other issues on this gem suggest that only using prompt: 'consent' and access_type: 'offline' together are supposed to be the only way to get a refresh_token, but that doesn't influence whether this or underlying gems check for a refresh_token once the access_token is expired.
  3. Additionally, is there a way to pass an expires_at, or expire_in argument to the gem for shortening the access_token expiration time for testing purposes?

I've looked into this issue across the multiple omniauth gem dependencies, and this gem for 5+ days, and haven't gotten anywhere. Any feedback or answers would be deeply appreciated 🙏 ❤️

zquestz commented 2 months ago

if you are only using this for login, then there is no need to log them out when the access_token expires. The only time you need to worry about refreshing the token is if you actually need to use it...

Also what exactly are you posting to the /auth/google/oauth2 endpoint? It shouldn't be seeing any access token, and therefore it wouldn't be expired... unless you are passing in the expired token.