Bug Description
On a Google Workspace OIDC deployment, hoop never obtains a refresh_token from Google in the first place, because GetAuthCodeURL only requests access_type=offline when the literal scope offline_access is present in the configured scopes:
https://github.com/hoophq/hoop/blob/main/gateway/idp/oidc/oidc.go#L240-L246
offline_access as a scope is an Auth0/Okta/Azure AD convention — Google never adopted it. Google signals "also return a refresh_token" exclusively via the access_type=offline query parameter, independent of scopes. So for a Google issuer, setting IDP_CUSTOM_SCOPES=offline_access is a no-op, no refresh_token is ever returned, and TryRefreshExpiredToken (gateway/idp/core.go) has nothing to work with once the ~1h Google access token expires.
Related to #1540, but a separate bug: that issue is about the gRPC keep-alive poll never attempting a refresh even when a valid refresh_token exists. This issue is about Google specifically never obtaining a refresh_token to begin with — so even after #1540's fix lands, a Google Workspace deployment would still be broken, since there'd be nothing to refresh with.
The codebase already has the correct Google-specific handling for a different, unrelated OAuth flow:
That code explicitly sets oauth2.AccessTypeOffline + forces prompt=consent for Google. This was just never applied to the main login flow.
Steps to Reproduce
- Deploy the gateway with
AUTH_METHOD=oidc,IDP_ISSUER=https://accounts.google.com, and Google Workspace as the IdP. - Log in via
hoop loginor the webapp. - Wait for the Google access token to expire (~1h) and for the gateway's 5-minute user-token poll to notice.
- Any active
hoop connectsession is terminated with "access token is expired"; the webapp session also requires a fresh interactive login.
Expected Behavior
The session should be silently renewed using a refresh_token, the way it already works (or should work, per #1540) for IdPs that follow the offline_access scope convention.
Actual Behavior
No refresh_token is ever requested from Google, so every session expiry requires a full interactive browser re-login.
Version
1.107.2
Browser(s)
N/A — this is a gateway/OIDC-provider issue, not browser-specific.
Operating System
N/A — server-side (gateway) issue; observed on a Linux/Docker deployment.
Screenshots
No response
Additional Context
Happy to submit a PR: detect the Google issuer (the codebase already does this exact check elsewhere for gsuite-groups sync, via the googleIssuerURL const at gateway/idp/oidc/oidc.go#L26,L111) and unconditionally request access_type=offline (+ prompt=consent on first login) for that issuer, instead of gating on the offline_access scope string.
Relevant Log Output
N/ACode of Conduct
- I agree to follow this project's Code of Conduct
🔗 Synced to Linear: SUP-164