Class: OmniAuth::Strategies::GoogleOauth2
- Inherits:
-
OAuth2
- Object
- OAuth2
- OmniAuth::Strategies::GoogleOauth2
- Defined in:
- lib/omniauth/strategies/google_oauth2.rb
Overview
Main class for Google OAuth2 strategy.
Constant Summary collapse
- ALLOWED_ISSUERS =
['accounts.google.com', 'https://accounts.google.com'].freeze
- BASE_SCOPE_URL =
'https://www.googleapis.com/auth/'- BASE_SCOPES =
%w[profile email openid].freeze
- DEFAULT_SCOPE =
'email,profile'- USER_INFO_URL =
'https://www.googleapis.com/oauth2/v3/userinfo'- JWKS_URL =
'https://www.googleapis.com/oauth2/v3/certs'- JWKS_CACHE_TTL =
3600- JWKS_RETRY_INTERVAL =
60- LOG_MESSAGE_LIMIT =
200- REQUIRED_ID_TOKEN_CLAIMS =
%w[iss aud exp sub].freeze
- AUTHORIZE_OPTIONS =
%i[access_type hd login_hint prompt request_visible_actions scope state redirect_uri include_granted_scopes enable_granular_consent openid_realm device_id device_name]
Class.new(StandardError)
Class Method Summary collapse
-
.cached_jwks(force: false, &fetch) ⇒ Object
Google's signing keys rotate, so the key set is shared across requests and refetched on expiry or when a token names a kid we do not hold.
- .reset_jwks_cache! ⇒ Object
Instance Method Summary collapse
- #authorize_params ⇒ Object
- #custom_build_access_token ⇒ Object (also: #build_access_token)
- #raw_info ⇒ Object
Class Method Details
.cached_jwks(force: false, &fetch) ⇒ Object
Google's signing keys rotate, so the key set is shared across requests and refetched on expiry or when a token names a kid we do not hold. The fetch deliberately happens under the lock: concurrent callers wait for one request rather than each issuing their own. The keys belong to Google, not to any one strategy class, so a subclass shares this cache rather than keeping a second one and refetching the same data. Delegating also keeps it from reaching for a mutex it does not own, which is only defined here.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/omniauth/strategies/google_oauth2.rb', line 39 def cached_jwks(force: false, &fetch) return GoogleOauth2.cached_jwks(force: force, &fetch) unless equal?(GoogleOauth2) @jwks_mutex.synchronize do now = ::Time.now.to_i # A forced refresh means some token named a kid we do not hold, which # the sender chooses freely. Honour it no more often than the retry # interval, or it becomes a way to drive unlimited fetches, each one # holding this lock while every other login waits. force &&= @jwks_forced_at.nil? || now >= @jwks_forced_at + JWKS_RETRY_INTERVAL if force || @jwks.nil? || now >= @jwks_expires_at.to_i # Back off even with nothing cached. Otherwise an unreachable # endpoint queues every waiting caller behind its own timeout, # since the lock serializes them and no expiry has been recorded. raise JwksUnavailable, 'within JWKS retry backoff' if @jwks.nil? && now < @jwks_retry_at.to_i @jwks_forced_at = now if force begin @jwks = yield @jwks_expires_at = ::Time.now.to_i + JWKS_CACHE_TTL rescue StandardError retry_at = ::Time.now.to_i + JWKS_RETRY_INTERVAL @jwks_retry_at = retry_at raise if @jwks.nil? # Google's keys outlive this cache by a wide margin, so serve the # stale set through a short outage rather than failing every # login, and back off instead of refetching on each request. @jwks_expires_at = retry_at end end @jwks end end |
.reset_jwks_cache! ⇒ Object
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/omniauth/strategies/google_oauth2.rb', line 77 def reset_jwks_cache! return GoogleOauth2.reset_jwks_cache! unless equal?(GoogleOauth2) @jwks_mutex.synchronize do @jwks = nil @jwks_expires_at = nil @jwks_retry_at = nil @jwks_forced_at = nil end end |
Instance Method Details
#authorize_params ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/omniauth/strategies/google_oauth2.rb', line 101 def super.tap do |params| ([:authorize_options] & [:overridable_authorize_options]).each do |k| params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s]) end params[:scope] = get_scope(params) params[:access_type] = 'offline' if params[:access_type].nil? params['openid.realm'] = params.delete(:openid_realm) unless params[:openid_realm].nil? session['omniauth.state'] = params[:state] if params[:state] end end |
#custom_build_access_token ⇒ Object Also known as: build_access_token
150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/omniauth/strategies/google_oauth2.rb', line 150 def custom_build_access_token access_token = get_access_token(request) # Nothing in the request produced a usable credential. Raising here gives # the caller an ordinary auth failure; the alternative is omniauth-oauth2 # calling #expired? on nil and the request dying with a NoMethodError. raise CallbackError.new(:invalid_credentials, 'No valid credentials were supplied in the callback request') if access_token.nil? verify_hd(access_token) access_token end |
#raw_info ⇒ Object
146 147 148 |
# File 'lib/omniauth/strategies/google_oauth2.rb', line 146 def raw_info @raw_info ||= access_token.get(USER_INFO_URL).parsed end |