googleauth 1.2.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -130,13 +130,8 @@ module Google
130
130
  end
131
131
  n_bn = OpenSSL::BN.new n_data, 2
132
132
  e_bn = OpenSSL::BN.new e_data, 2
133
- rsa_key = OpenSSL::PKey::RSA.new
134
- if rsa_key.respond_to? :set_key
135
- rsa_key.set_key n_bn, e_bn, nil
136
- else
137
- rsa_key.n = n_bn
138
- rsa_key.e = e_bn
139
- end
133
+ sequence = [OpenSSL::ASN1::Integer.new(n_bn), OpenSSL::ASN1::Integer.new(e_bn)]
134
+ rsa_key = OpenSSL::PKey::RSA.new OpenSSL::ASN1::Sequence(sequence).to_der
140
135
  rsa_key.public_key
141
136
  end
142
137
 
@@ -161,9 +156,13 @@ module Google
161
156
  x_hex = x_data.unpack1 "H*"
162
157
  y_hex = y_data.unpack1 "H*"
163
158
  bn = OpenSSL::BN.new ["04#{x_hex}#{y_hex}"].pack("H*"), 2
164
- key = OpenSSL::PKey::EC.new curve_name
165
- key.public_key = OpenSSL::PKey::EC::Point.new group, bn
166
- key
159
+ point = OpenSSL::PKey::EC::Point.new group, bn
160
+ sequence = OpenSSL::ASN1::Sequence([
161
+ OpenSSL::ASN1::Sequence([OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
162
+ OpenSSL::ASN1::ObjectId(curve_name)]),
163
+ OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
164
+ ])
165
+ OpenSSL::PKey::EC.new sequence.to_der
167
166
  end
168
167
  end
169
168
  end
@@ -153,7 +153,7 @@ module Google
153
153
  # one of the provided values, or the verification will fail with
154
154
  # {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
155
155
  # (the default), no azp checking is performed.
156
- # @param aud [String,Array<String>,nil] The expected audience. At least
156
+ # @param iss [String,Array<String>,nil] The expected issuer. At least
157
157
  # one `iss` field in the token must match at least one of the
158
158
  # provided issuers, or the verification will fail with
159
159
  # {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
@@ -191,7 +191,7 @@ module Google
191
191
  # one of the provided values, or the verification will fail with
192
192
  # {Google::Auth::IDToken::AuthorizedPartyMismatchError}. If `nil`
193
193
  # (the default), no azp checking is performed.
194
- # @param aud [String,Array<String>,nil] The expected audience. At least
194
+ # @param iss [String,Array<String>,nil] The expected issuer. At least
195
195
  # one `iss` field in the token must match at least one of the
196
196
  # provided issuers, or the verification will fail with
197
197
  # {Google::Auth::IDToken::IssuerMismatchError}. If `nil`, no issuer
@@ -27,7 +27,8 @@ module Google
27
27
  json_key["private_key"],
28
28
  json_key["client_email"],
29
29
  json_key["project_id"],
30
- json_key["quota_project_id"]
30
+ json_key["quota_project_id"],
31
+ json_key["universe_domain"]
31
32
  ]
32
33
  end
33
34
  end
@@ -0,0 +1,109 @@
1
+ # Copyright 2023 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "googleauth/helpers/connection"
16
+
17
+ module Google
18
+ module Auth
19
+ module OAuth2
20
+ # OAuth 2.0 Token Exchange Spec.
21
+ # This module defines a token exchange utility based on the
22
+ # [OAuth 2.0 Token Exchange](https://tools.ietf.org/html/rfc8693) spec. This will be mainly
23
+ # used to exchange external credentials for GCP access tokens in workload identity pools to
24
+ # access Google APIs.
25
+ # The implementation will support various types of client authentication as allowed in the spec.
26
+ #
27
+ # A deviation on the spec will be for additional Google specific options that cannot be easily
28
+ # mapped to parameters defined in the RFC.
29
+ # The returned dictionary response will be based on the [rfc8693 section 2.2.1]
30
+ # (https://tools.ietf.org/html/rfc8693#section-2.2.1) spec JSON response.
31
+ #
32
+ class STSClient
33
+ include Helpers::Connection
34
+
35
+ URLENCODED_HEADERS = { "Content-Type": "application/x-www-form-urlencoded" }.freeze
36
+
37
+ # Create a new instance of the STSClient.
38
+ #
39
+ # @param [String] token_exchange_endpoint
40
+ # The token exchange endpoint.
41
+ def initialize options = {}
42
+ raise "Token exchange endpoint can not be nil" if options[:token_exchange_endpoint].nil?
43
+ self.default_connection = options[:connection]
44
+ @token_exchange_endpoint = options[:token_exchange_endpoint]
45
+ end
46
+
47
+ # Exchanges the provided token for another type of token based on the
48
+ # rfc8693 spec
49
+ #
50
+ # @param [Faraday instance] connection
51
+ # A callable faraday instance used to make HTTP requests.
52
+ # @param [String] grant_type
53
+ # The OAuth 2.0 token exchange grant type.
54
+ # @param [String] subject_token
55
+ # The OAuth 2.0 token exchange subject token.
56
+ # @param [String] subject_token_type
57
+ # The OAuth 2.0 token exchange subject token type.
58
+ # @param [String] resource
59
+ # The optional OAuth 2.0 token exchange resource field.
60
+ # @param [String] audience
61
+ # The optional OAuth 2.0 token exchange audience field.
62
+ # @param [Array<String>] scopes
63
+ # The optional list of scopes to use.
64
+ # @param [String] requested_token_type
65
+ # The optional OAuth 2.0 token exchange requested token type.
66
+ # @param additional_headers (Hash<String,String>):
67
+ # The optional additional headers to pass to the token exchange endpoint.
68
+ #
69
+ # @return [Hash] A hash containing the token exchange response.
70
+ def exchange_token options = {}
71
+ missing_required_opts = [:grant_type, :subject_token, :subject_token_type] - options.keys
72
+ unless missing_required_opts.empty?
73
+ raise ArgumentError, "Missing required options: #{missing_required_opts.join ', '}"
74
+ end
75
+
76
+ # TODO: Add the ability to add authentication to the headers
77
+ headers = URLENCODED_HEADERS.dup.merge(options[:additional_headers] || {})
78
+
79
+ request_body = make_request options
80
+
81
+ response = connection.post @token_exchange_endpoint, URI.encode_www_form(request_body), headers
82
+
83
+ if response.status != 200
84
+ raise "Token exchange failed with status #{response.status}"
85
+ end
86
+
87
+ MultiJson.load response.body
88
+ end
89
+
90
+ private
91
+
92
+ def make_request options = {}
93
+ request_body = {
94
+ grant_type: options[:grant_type],
95
+ audience: options[:audience],
96
+ scope: Array(options[:scopes])&.join(" ") || [],
97
+ requested_token_type: options[:requested_token_type],
98
+ subject_token: options[:subject_token],
99
+ subject_token_type: options[:subject_token_type]
100
+ }
101
+ unless options[:additional_options].nil?
102
+ request_body[:options] = CGI.escape MultiJson.dump(options[:additional_options], symbolize_name: true)
103
+ end
104
+ request_body
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -18,27 +18,60 @@ require "multi_json"
18
18
 
19
19
  module Google
20
20
  module Auth
21
- # Small utility for normalizing scopes into canonical form
21
+ ##
22
+ # Small utility for normalizing scopes into canonical form.
23
+ #
24
+ # The canonical form of scopes is as an array of strings, each in the form
25
+ # of a full URL. This utility converts space-delimited scope strings into
26
+ # this form, and handles a small number of common aliases.
27
+ #
28
+ # This is used by UserRefreshCredentials to verify that a credential grants
29
+ # a requested scope.
30
+ #
22
31
  module ScopeUtil
32
+ ##
33
+ # Aliases understood by this utility
34
+ #
23
35
  ALIASES = {
24
36
  "email" => "https://www.googleapis.com/auth/userinfo.email",
25
37
  "profile" => "https://www.googleapis.com/auth/userinfo.profile",
26
38
  "openid" => "https://www.googleapis.com/auth/plus.me"
27
39
  }.freeze
28
40
 
41
+ ##
42
+ # Normalize the input, which may be an array of scopes or a whitespace-
43
+ # delimited scope string. The output is always an array, even if a single
44
+ # scope is input.
45
+ #
46
+ # @param scope [String,Array<String>] Input scope(s)
47
+ # @return [Array<String>] An array of scopes in canonical form.
48
+ #
29
49
  def self.normalize scope
30
50
  list = as_array scope
31
51
  list.map { |item| ALIASES[item] || item }
32
52
  end
33
53
 
54
+ ##
55
+ # Ensure the input is an array. If a single string is passed in, splits
56
+ # it via whitespace. Does not interpret aliases.
57
+ #
58
+ # @param scope [String,Array<String>] Input scope(s)
59
+ # @return [Array<String>] Always an array of strings
60
+ # @raise ArgumentError If the input is not a string or array of strings
61
+ #
34
62
  def self.as_array scope
35
63
  case scope
36
64
  when Array
65
+ scope.each do |item|
66
+ unless item.is_a? String
67
+ raise ArgumentError, "Invalid scope value: #{item.inspect}. Must be string or array"
68
+ end
69
+ end
37
70
  scope
38
71
  when String
39
72
  scope.split
40
73
  else
41
- raise "Invalid scope value. Must be string or array"
74
+ raise ArgumentError, "Invalid scope value: #{scope.inspect}. Must be string or array"
42
75
  end
43
76
  end
44
77
  end
@@ -39,7 +39,11 @@ module Google
39
39
  attr_reader :quota_project_id
40
40
 
41
41
  def enable_self_signed_jwt?
42
- @enable_self_signed_jwt
42
+ # Use a self-singed JWT if there's no information that can be used to
43
+ # obtain an OAuth token, OR if there are scopes but also an assertion
44
+ # that they are default scopes that shouldn't be used to fetch a token,
45
+ # OR we are not in the default universe and thus OAuth isn't supported.
46
+ target_audience.nil? && (scope.nil? || @enable_self_signed_jwt || universe_domain != "googleapis.com")
43
47
  end
44
48
 
45
49
  # Creates a ServiceAccountCredentials.
@@ -53,12 +57,13 @@ module Google
53
57
  raise ArgumentError, "Cannot specify both scope and target_audience" if scope && target_audience
54
58
 
55
59
  if json_key_io
56
- private_key, client_email, project_id, quota_project_id = read_json_key json_key_io
60
+ private_key, client_email, project_id, quota_project_id, universe_domain = read_json_key json_key_io
57
61
  else
58
62
  private_key = unescape ENV[CredentialsLoader::PRIVATE_KEY_VAR]
59
63
  client_email = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
60
64
  project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
61
65
  quota_project_id = nil
66
+ universe_domain = nil
62
67
  end
63
68
  project_id ||= CredentialsLoader.load_gcloud_project_id
64
69
 
@@ -70,7 +75,8 @@ module Google
70
75
  issuer: client_email,
71
76
  signing_key: OpenSSL::PKey::RSA.new(private_key),
72
77
  project_id: project_id,
73
- quota_project_id: quota_project_id)
78
+ quota_project_id: quota_project_id,
79
+ universe_domain: universe_domain || "googleapis.com")
74
80
  .configure_connection(options)
75
81
  end
76
82
 
@@ -93,16 +99,18 @@ module Google
93
99
  # Extends the base class to use a transient
94
100
  # ServiceAccountJwtHeaderCredentials for certain cases.
95
101
  def apply! a_hash, opts = {}
96
- # Use a self-singed JWT if there's no information that can be used to
97
- # obtain an OAuth token, OR if there are scopes but also an assertion
98
- # that they are default scopes that shouldn't be used to fetch a token.
99
- if target_audience.nil? && (scope.nil? || enable_self_signed_jwt?)
102
+ if enable_self_signed_jwt?
100
103
  apply_self_signed_jwt! a_hash
101
104
  else
102
105
  super
103
106
  end
104
107
  end
105
108
 
109
+ # Modifies this logic so it also requires self-signed-jwt to be disabled
110
+ def needs_access_token?
111
+ super && !enable_self_signed_jwt?
112
+ end
113
+
106
114
  private
107
115
 
108
116
  def apply_self_signed_jwt! a_hash
@@ -130,7 +138,7 @@ module Google
130
138
  # cf [Application Default Credentials](https://cloud.google.com/docs/authentication/production)
131
139
  class ServiceAccountJwtHeaderCredentials
132
140
  JWT_AUD_URI_KEY = :jwt_aud_uri
133
- AUTH_METADATA_KEY = Signet::OAuth2::AUTH_METADATA_KEY
141
+ AUTH_METADATA_KEY = Google::Auth::BaseClient::AUTH_METADATA_KEY
134
142
  TOKEN_CRED_URI = "https://www.googleapis.com/oauth2/v4/token".freeze
135
143
  SIGNING_ALGORITHM = "RS256".freeze
136
144
  EXPIRY = 60
@@ -138,6 +146,7 @@ module Google
138
146
  extend JsonKeyReader
139
147
  attr_reader :project_id
140
148
  attr_reader :quota_project_id
149
+ attr_accessor :universe_domain
141
150
 
142
151
  # Create a ServiceAccountJwtHeaderCredentials.
143
152
  #
@@ -154,14 +163,16 @@ module Google
154
163
  def initialize options = {}
155
164
  json_key_io = options[:json_key_io]
156
165
  if json_key_io
157
- @private_key, @issuer, @project_id, @quota_project_id =
166
+ @private_key, @issuer, @project_id, @quota_project_id, @universe_domain =
158
167
  self.class.read_json_key json_key_io
159
168
  else
160
169
  @private_key = ENV[CredentialsLoader::PRIVATE_KEY_VAR]
161
170
  @issuer = ENV[CredentialsLoader::CLIENT_EMAIL_VAR]
162
171
  @project_id = ENV[CredentialsLoader::PROJECT_ID_VAR]
163
172
  @quota_project_id = nil
173
+ @universe_domain = nil
164
174
  end
175
+ @universe_domain ||= "googleapis.com"
165
176
  @project_id ||= CredentialsLoader.load_gcloud_project_id
166
177
  @signing_key = OpenSSL::PKey::RSA.new @private_key
167
178
  @scope = options[:scope]
@@ -192,8 +203,6 @@ module Google
192
203
  proc { |a_hash, opts = {}| apply a_hash, opts }
193
204
  end
194
205
 
195
- protected
196
-
197
206
  # Creates a jwt uri token.
198
207
  def new_jwt_token jwt_aud_uri = nil, options = {}
199
208
  now = Time.new
@@ -212,6 +221,11 @@ module Google
212
221
 
213
222
  JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
214
223
  end
224
+
225
+ # Duck-types the corresponding method from BaseClient
226
+ def needs_access_token?
227
+ false
228
+ end
215
229
  end
216
230
  end
217
231
  end
@@ -13,16 +13,27 @@
13
13
  # limitations under the License.
14
14
 
15
15
  require "signet/oauth_2/client"
16
+ require "googleauth/base_client"
16
17
 
17
18
  module Signet
18
19
  # OAuth2 supports OAuth2 authentication.
19
20
  module OAuth2
20
- AUTH_METADATA_KEY = :authorization
21
21
  # Signet::OAuth2::Client creates an OAuth2 client
22
22
  #
23
23
  # This reopens Client to add #apply and #apply! methods which update a
24
24
  # hash with the fetched authentication token.
25
25
  class Client
26
+ include Google::Auth::BaseClient
27
+
28
+ alias update_token_signet_base update_token!
29
+
30
+ def update_token! options = {}
31
+ options = deep_hash_normalize options
32
+ update_token_signet_base options
33
+ self.universe_domain = options[:universe_domain] if options.key? :universe_domain
34
+ self
35
+ end
36
+
26
37
  def configure_connection options
27
38
  @connection_info =
28
39
  options[:connection_builder] || options[:default_connection]
@@ -34,36 +45,8 @@ module Signet
34
45
  target_audience ? :id_token : :access_token
35
46
  end
36
47
 
37
- # Whether the id_token or access_token is missing or about to expire.
38
- def needs_access_token?
39
- send(token_type).nil? || expires_within?(60)
40
- end
41
-
42
- # Updates a_hash updated with the authentication token
43
- def apply! a_hash, opts = {}
44
- # fetch the access token there is currently not one, or if the client
45
- # has expired
46
- fetch_access_token! opts if needs_access_token?
47
- a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
48
- end
49
-
50
- # Returns a clone of a_hash updated with the authentication token
51
- def apply a_hash, opts = {}
52
- a_copy = a_hash.clone
53
- apply! a_copy, opts
54
- a_copy
55
- end
56
-
57
- # Returns a reference to the #apply method, suitable for passing as
58
- # a closure
59
- def updater_proc
60
- proc { |a_hash, opts = {}| apply a_hash, opts }
61
- end
62
-
63
- def on_refresh &block
64
- @refresh_listeners = [] unless defined? @refresh_listeners
65
- @refresh_listeners << block
66
- end
48
+ # Set the universe domain
49
+ attr_accessor :universe_domain
67
50
 
68
51
  alias orig_fetch_access_token! fetch_access_token!
69
52
  def fetch_access_token! options = {}
@@ -78,13 +61,6 @@ module Signet
78
61
  info
79
62
  end
80
63
 
81
- def notify_refresh_listeners
82
- listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
83
- listeners.each do |block|
84
- block.call self
85
- end
86
- end
87
-
88
64
  def build_default_connection
89
65
  if !defined?(@connection_info)
90
66
  nil
@@ -16,6 +16,7 @@ require "uri"
16
16
  require "multi_json"
17
17
  require "googleauth/signet"
18
18
  require "googleauth/user_refresh"
19
+ require "securerandom"
19
20
 
20
21
  module Google
21
22
  module Auth
@@ -54,17 +55,26 @@ module Google
54
55
  # Authorization scope to request
55
56
  # @param [Google::Auth::Stores::TokenStore] token_store
56
57
  # Backing storage for persisting user credentials
57
- # @param [String] callback_uri
58
+ # @param [String] legacy_callback_uri
58
59
  # URL (either absolute or relative) of the auth callback.
59
- # Defaults to '/oauth2callback'
60
- def initialize client_id, scope, token_store, callback_uri = nil
60
+ # Defaults to '/oauth2callback'.
61
+ # @deprecated This field is deprecated. Instead, use the keyword
62
+ # argument callback_uri.
63
+ # @param [String] code_verifier
64
+ # Random string of 43-128 chars used to verify the key exchange using
65
+ # PKCE.
66
+ def initialize client_id, scope, token_store,
67
+ legacy_callback_uri = nil,
68
+ callback_uri: nil,
69
+ code_verifier: nil
61
70
  raise NIL_CLIENT_ID_ERROR if client_id.nil?
62
71
  raise NIL_SCOPE_ERROR if scope.nil?
63
72
 
64
73
  @client_id = client_id
65
74
  @scope = Array(scope)
66
75
  @token_store = token_store
67
- @callback_uri = callback_uri || "/oauth2callback"
76
+ @callback_uri = legacy_callback_uri || callback_uri || "/oauth2callback"
77
+ @code_verifier = code_verifier
68
78
  end
69
79
 
70
80
  # Build the URL for requesting authorization.
@@ -80,14 +90,29 @@ module Google
80
90
  # @param [String, Array<String>] scope
81
91
  # Authorization scope to request. Overrides the instance scopes if not
82
92
  # nil.
93
+ # @param [Hash] additional_parameters
94
+ # Additional query parameters to be added to the authorization URL.
83
95
  # @return [String]
84
96
  # Authorization url
85
97
  def get_authorization_url options = {}
86
98
  scope = options[:scope] || @scope
99
+
100
+ options[:additional_parameters] ||= {}
101
+
102
+ if @code_verifier
103
+ options[:additional_parameters].merge!(
104
+ {
105
+ code_challenge: generate_code_challenge(@code_verifier),
106
+ code_challenge_method: code_challenge_method
107
+ }
108
+ )
109
+ end
110
+
87
111
  credentials = UserRefreshCredentials.new(
88
112
  client_id: @client_id.id,
89
113
  client_secret: @client_id.secret,
90
- scope: scope
114
+ scope: scope,
115
+ additional_parameters: options[:additional_parameters]
91
116
  )
92
117
  redirect_uri = redirect_uri_for options[:base_url]
93
118
  url = credentials.authorization_uri(access_type: "offline",
@@ -144,6 +169,9 @@ module Google
144
169
  # Absolute URL to resolve the configured callback uri against.
145
170
  # Required if the configured
146
171
  # callback uri is a relative.
172
+ # @param [Hash] additional_parameters
173
+ # Additional parameters to be added to the post body of token
174
+ # endpoint request.
147
175
  # @return [Google::Auth::UserRefreshCredentials]
148
176
  # Credentials if exchange is successful
149
177
  def get_credentials_from_code options = {}
@@ -151,11 +179,14 @@ module Google
151
179
  code = options[:code]
152
180
  scope = options[:scope] || @scope
153
181
  base_url = options[:base_url]
182
+ options[:additional_parameters] ||= {}
183
+ options[:additional_parameters].merge!({ code_verifier: @code_verifier })
154
184
  credentials = UserRefreshCredentials.new(
155
- client_id: @client_id.id,
156
- client_secret: @client_id.secret,
157
- redirect_uri: redirect_uri_for(base_url),
158
- scope: scope
185
+ client_id: @client_id.id,
186
+ client_secret: @client_id.secret,
187
+ redirect_uri: redirect_uri_for(base_url),
188
+ scope: scope,
189
+ additional_parameters: options[:additional_parameters]
159
190
  )
160
191
  credentials.code = code
161
192
  credentials.fetch_access_token!({})
@@ -221,6 +252,23 @@ module Google
221
252
  credentials
222
253
  end
223
254
 
255
+ # The code verifier for PKCE for OAuth 2.0. When set, the
256
+ # authorization URI will contain the Code Challenge and Code
257
+ # Challenge Method querystring parameters, and the token URI will
258
+ # contain the Code Verifier parameter.
259
+ #
260
+ # @param [String|nil] new_code_erifier
261
+ def code_verifier= new_code_verifier
262
+ @code_verifier = new_code_verifier
263
+ end
264
+
265
+ # Generate the code verifier needed to be sent while fetching
266
+ # authorization URL.
267
+ def self.generate_code_verifier
268
+ random_number = rand 32..96
269
+ SecureRandom.alphanumeric random_number
270
+ end
271
+
224
272
  private
225
273
 
226
274
  # @private Fetch stored token with given user_id
@@ -265,6 +313,15 @@ module Google
265
313
  def uri_is_postmessage? uri
266
314
  uri.to_s.casecmp("postmessage").zero?
267
315
  end
316
+
317
+ def generate_code_challenge code_verifier
318
+ digest = Digest::SHA256.digest code_verifier
319
+ Base64.urlsafe_encode64 digest, padding: false
320
+ end
321
+
322
+ def code_challenge_method
323
+ "S256"
324
+ end
268
325
  end
269
326
  end
270
327
  end
@@ -50,7 +50,8 @@ module Google
50
50
  "client_secret" => ENV[CredentialsLoader::CLIENT_SECRET_VAR],
51
51
  "refresh_token" => ENV[CredentialsLoader::REFRESH_TOKEN_VAR],
52
52
  "project_id" => ENV[CredentialsLoader::PROJECT_ID_VAR],
53
- "quota_project_id" => nil
53
+ "quota_project_id" => nil,
54
+ "universe_domain" => nil
54
55
  }
55
56
  new(token_credential_uri: TOKEN_CRED_URI,
56
57
  client_id: user_creds["client_id"],
@@ -58,7 +59,8 @@ module Google
58
59
  refresh_token: user_creds["refresh_token"],
59
60
  project_id: user_creds["project_id"],
60
61
  quota_project_id: user_creds["quota_project_id"],
61
- scope: scope)
62
+ scope: scope,
63
+ universe_domain: user_creds["universe_domain"] || "googleapis.com")
62
64
  .configure_connection(options)
63
65
  end
64
66
 
@@ -16,6 +16,6 @@ module Google
16
16
  # Module Auth provides classes that provide Google-specific authorization
17
17
  # used to access Google APIs.
18
18
  module Auth
19
- VERSION = "1.2.0".freeze
19
+ VERSION = "1.11.0".freeze
20
20
  end
21
21
  end
@@ -93,11 +93,22 @@ module Google
93
93
  # Authorization scope to request
94
94
  # @param [Google::Auth::Stores::TokenStore] token_store
95
95
  # Backing storage for persisting user credentials
96
- # @param [String] callback_uri
96
+ # @param [String] legacy_callback_uri
97
97
  # URL (either absolute or relative) of the auth callback. Defaults
98
- # to '/oauth2callback'
99
- def initialize client_id, scope, token_store, callback_uri = nil
100
- super client_id, scope, token_store, callback_uri
98
+ # to '/oauth2callback'.
99
+ # @deprecated This field is deprecated. Instead, use the keyword
100
+ # argument callback_uri.
101
+ # @param [String] code_verifier
102
+ # Random string of 43-128 chars used to verify the key exchange using
103
+ # PKCE.
104
+ def initialize client_id, scope, token_store,
105
+ legacy_callback_uri = nil,
106
+ callback_uri: nil,
107
+ code_verifier: nil
108
+ super client_id, scope, token_store,
109
+ legacy_callback_uri,
110
+ code_verifier: code_verifier,
111
+ callback_uri: callback_uri
101
112
  end
102
113
 
103
114
  # Handle the result of the oauth callback. Exchanges the authorization
@@ -192,13 +203,13 @@ module Google
192
203
  end
193
204
 
194
205
  def self.extract_callback_state request
195
- state = MultiJson.load(request[STATE_PARAM] || "{}")
206
+ state = MultiJson.load(request.params[STATE_PARAM] || "{}")
196
207
  redirect_uri = state[CURRENT_URI_KEY]
197
208
  callback_state = {
198
- AUTH_CODE_KEY => request[AUTH_CODE_KEY],
199
- ERROR_CODE_KEY => request[ERROR_CODE_KEY],
209
+ AUTH_CODE_KEY => request.params[AUTH_CODE_KEY],
210
+ ERROR_CODE_KEY => request.params[ERROR_CODE_KEY],
200
211
  SESSION_ID_KEY => state[SESSION_ID_KEY],
201
- SCOPE_KEY => request[SCOPE_KEY]
212
+ SCOPE_KEY => request.params[SCOPE_KEY]
202
213
  }
203
214
  [callback_state, redirect_uri]
204
215
  end