googleauth 1.3.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bc48c47d78d7ec955a2a5557fc8f1cff502a28dd1e18c5af3fc566be5743171
4
- data.tar.gz: 220a8fed81a73d5bc93a2fca2951a749b9469cb769a198cf13564ad7f714ac90
3
+ metadata.gz: 4912a601c0a234fa9faf150d7461cab993f775b715f6ac7d19db017ceae74e6d
4
+ data.tar.gz: e08da21e12d58260944079d068a04099fa0c812eb486e760a3ab12dd002700f4
5
5
  SHA512:
6
- metadata.gz: 73f52ffce21a05e15102b54aabbcb3cb199d32e9caf318b125b48b6caeddc01f77c3de4ea09513b0b1e9e503c912e55adf5864b4295b86af0620aa0c7df25df4
7
- data.tar.gz: 7ec107faa35d72aa1fd8e79b86b30df9acf061ce86ac52641bc12b69391f0b4f2adde8021b908327e379dee65c1e2ed7ed1b203e629ca1f4d25a988e80c31eb2
6
+ metadata.gz: b0346fcaf38cb783fd4d22f0734994298d63dfa1a89fd34df4d4f42b87160de410e34c93ab4773c3bbaf03b41160f10e3fd5bc0fa137d1ab6fb5dce15f72ba53
7
+ data.tar.gz: c5ff10a04491e9f56dff9bcea24c67398e67713efc89c2d378075621da837b59c5fdbd36c0b97d5753fd0358befe8ce2ceacc86af1cce0a1c54d609d916903c6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,98 @@
1
1
  # Release History
2
2
 
3
+ ### 1.11.0 (2024-02-09)
4
+
5
+ #### Features
6
+
7
+ * Deprecate the positional argument for callback_uri, and introduce keyword argument instead ([#475](https://github.com/googleapis/google-auth-library-ruby/issues/475))
8
+
9
+ ### 1.10.0 (2024-02-08)
10
+
11
+ #### Features
12
+
13
+ * add PKCE to 3 Legged OAuth exchange ([#471](https://github.com/googleapis/google-auth-library-ruby/issues/471))
14
+ #### Bug Fixes
15
+
16
+ * Client library credentials provide correct self-signed JWT and external account behavior when loading from a file path or JSON data ([#474](https://github.com/googleapis/google-auth-library-ruby/issues/474))
17
+ * Prioritize universe domain specified in GCECredentials arguments over metadata-fetched value ([#472](https://github.com/googleapis/google-auth-library-ruby/issues/472))
18
+
19
+ ### 1.9.2 (2024-01-25)
20
+
21
+ #### Bug Fixes
22
+
23
+ * Prevent access tokens from being fetched at service account construction in the self-signed-jwt case ([#467](https://github.com/googleapis/google-auth-library-ruby/issues/467))
24
+
25
+ ### 1.9.1 (2023-12-12)
26
+
27
+ #### Bug Fixes
28
+
29
+ * update expires_in for cached metadata-retrieved tokens ([#464](https://github.com/googleapis/google-auth-library-ruby/issues/464))
30
+
31
+ ### 1.9.0 (2023-12-07)
32
+
33
+ #### Features
34
+
35
+ * Include universe_domain in credentials ([#460](https://github.com/googleapis/google-auth-library-ruby/issues/460))
36
+ * Use google-cloud-env for more robust Metadata Service access ([#459](https://github.com/googleapis/google-auth-library-ruby/issues/459))
37
+
38
+ ### 1.8.1 (2023-09-19)
39
+
40
+ #### Documentation
41
+
42
+ * improve ADC related error and warning messages ([#452](https://github.com/googleapis/google-auth-library-ruby/issues/452))
43
+
44
+ ### 1.8.0 (2023-09-07)
45
+
46
+ #### Features
47
+
48
+ * Pass additional parameters to auhtorization url ([#447](https://github.com/googleapis/google-auth-library-ruby/issues/447))
49
+ #### Documentation
50
+
51
+ * improve ADC related error and warning messages ([#449](https://github.com/googleapis/google-auth-library-ruby/issues/449))
52
+
53
+ ### 1.7.0 (2023-07-14)
54
+
55
+ #### Features
56
+
57
+ * Adding support for pluggable auth credentials ([#437](https://github.com/googleapis/google-auth-library-ruby/issues/437))
58
+ #### Documentation
59
+
60
+ * fixed iss argument and description in comments of IDTokens ([#438](https://github.com/googleapis/google-auth-library-ruby/issues/438))
61
+
62
+ ### 1.6.0 (2023-06-20)
63
+
64
+ #### Features
65
+
66
+ * adding identity pool credentials ([#433](https://github.com/googleapis/google-auth-library-ruby/issues/433))
67
+ #### Documentation
68
+
69
+ * deprecation message for discontinuing command line auth flow ([#435](https://github.com/googleapis/google-auth-library-ruby/issues/435))
70
+
71
+ ### 1.5.2 (2023-04-13)
72
+
73
+ #### Bug Fixes
74
+
75
+ * AWS IMDSV2 session token fetching shall call PUT method instead of GET ([#429](https://github.com/googleapis/google-auth-library-ruby/issues/429))
76
+ * GCECredentials - Allow retrieval of ID token ([#425](https://github.com/googleapis/google-auth-library-ruby/issues/425))
77
+
78
+ ### 1.5.1 (2023-04-10)
79
+
80
+ #### Bug Fixes
81
+
82
+ * Remove external account config validation ([#427](https://github.com/googleapis/google-auth-library-ruby/issues/427))
83
+
84
+ ### 1.5.0 (2023-03-21)
85
+
86
+ #### Features
87
+
88
+ * Add support for AWS Workload Identity Federation ([#418](https://github.com/googleapis/google-auth-library-ruby/issues/418))
89
+
90
+ ### 1.4.0 (2022-12-14)
91
+
92
+ #### Features
93
+
94
+ * make new_jwt_token public in order to fetch raw token directly ([#405](https://github.com/googleapis/google-auth-library-ruby/issues/405))
95
+
3
96
  ### 1.3.0 (2022-10-18)
4
97
 
5
98
  #### Features
data/README.md CHANGED
@@ -97,7 +97,48 @@ get('/oauth2callback') do
97
97
  end
98
98
  ```
99
99
 
100
- ### Example (Command Line)
100
+ ### Example (Web with PKCE)
101
+
102
+ Proof Key for Code Exchange (PKCE) is an [RFC](https://www.rfc-editor.org/rfc/rfc7636) that aims to prevent malicious operating system processes from hijacking an OAUTH 2.0 exchange. PKCE mitigates the above vulnerability by including `code_challenge` and `code_challenge_method` parameters in the Authorization Request and a `code_verifier` parameter in the Access Token Request.
103
+
104
+ ```ruby
105
+ require 'googleauth'
106
+ require 'googleauth/web_user_authorizer'
107
+ require 'googleauth/stores/redis_token_store'
108
+ require 'redis'
109
+
110
+ client_id = Google::Auth::ClientId.from_file('/path/to/client_secrets.json')
111
+ scope = ['https://www.googleapis.com/auth/drive']
112
+ token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
113
+ authorizer = Google::Auth::WebUserAuthorizer.new(
114
+ client_id, scope, token_store, '/oauth2callback')
115
+
116
+
117
+ get('/authorize') do
118
+ # NOTE: Assumes the user is already authenticated to the app
119
+ user_id = request.session['user_id']
120
+ # User needs to take care of generating the code_verifier and storing it in
121
+ # the session.
122
+ request.session['code_verifier'] ||= Google::Auth::WebUserAuthorizer.generate_code_verifier
123
+ authorizer.code_verifier = request.session['code_verifier']
124
+ credentials = authorizer.get_credentials(user_id, request)
125
+ if credentials.nil?
126
+ redirect authorizer.get_authorization_url(login_hint: user_id, request: request)
127
+ end
128
+ # Credentials are valid, can call APIs
129
+ # ...
130
+ end
131
+
132
+ get('/oauth2callback') do
133
+ target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(
134
+ request)
135
+ redirect target_url
136
+ end
137
+ ```
138
+
139
+ ### Example (Command Line) [Deprecated]
140
+
141
+ The Google Auth OOB flow has been discontiued on January 31, 2023. The OOB flow is a legacy flow that is no longer considered secure. To continue using Google Auth, please migrate your applications to a more secure flow. For more information on how to do this, please refer to this [OOB Migration](https://developers.google.com/identity/protocols/oauth2/resources/oob-migration) guide.
101
142
 
102
143
  ```ruby
103
144
  require 'googleauth'
@@ -241,6 +282,6 @@ hesitate to
241
282
  [ask questions](http://stackoverflow.com/questions/tagged/google-auth-library-ruby)
242
283
  about the client or APIs on [StackOverflow](http://stackoverflow.com).
243
284
 
244
- [application default credentials]: https://developers.google.com/accounts/docs/application-default-credentials
285
+ [application default credentials]: https://cloud.google.com/docs/authentication/provide-credentials-adc
245
286
  [contributing]: https://github.com/googleapis/google-auth-library-ruby/tree/main/.github/CONTRIBUTING.md
246
287
  [license]: https://github.com/googleapis/google-auth-library-ruby/tree/main/LICENSE
@@ -20,9 +20,9 @@ module Google
20
20
  # used to access Google APIs.
21
21
  module Auth
22
22
  NOT_FOUND_ERROR = <<~ERROR_MESSAGE.freeze
23
- Could not load the default credentials. Browse to
24
- https://developers.google.com/accounts/docs/application-default-credentials
25
- for more information
23
+ Your credentials were not found. To set up Application Default
24
+ Credentials for your environment, see
25
+ https://cloud.google.com/docs/authentication/external/set-up-adc
26
26
  ERROR_MESSAGE
27
27
 
28
28
  module_function
@@ -55,12 +55,8 @@ module Google
55
55
  DefaultCredentials.from_well_known_path(scope, options) ||
56
56
  DefaultCredentials.from_system_default_path(scope, options)
57
57
  return creds unless creds.nil?
58
- unless GCECredentials.on_gce? options
59
- # Clear cache of the result of GCECredentials.on_gce?
60
- GCECredentials.unmemoize_all
61
- raise NOT_FOUND_ERROR
62
- end
63
- GCECredentials.new scope: scope
58
+ raise NOT_FOUND_ERROR unless GCECredentials.on_gce? options
59
+ GCECredentials.new options.merge(scope: scope)
64
60
  end
65
61
  end
66
62
  end
@@ -0,0 +1,80 @@
1
+ # Copyright 2023 Google, Inc.
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
+ module Google
16
+ # Module Auth provides classes that provide Google-specific authorization
17
+ # used to access Google APIs.
18
+ module Auth
19
+ # BaseClient is a class used to contain common methods that are required by any
20
+ # Credentials Client, including AwsCredentials, ServiceAccountCredentials,
21
+ # and UserRefreshCredentials. This is a superclass of Signet::OAuth2::Client
22
+ # and has been created to create a generic interface for all credentials clients
23
+ # to use, including ones which do not inherit from Signet::OAuth2::Client.
24
+ module BaseClient
25
+ AUTH_METADATA_KEY = :authorization
26
+
27
+ # Updates a_hash updated with the authentication token
28
+ def apply! a_hash, opts = {}
29
+ # fetch the access token there is currently not one, or if the client
30
+ # has expired
31
+ fetch_access_token! opts if needs_access_token?
32
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
33
+ end
34
+
35
+ # Returns a clone of a_hash updated with the authentication token
36
+ def apply a_hash, opts = {}
37
+ a_copy = a_hash.clone
38
+ apply! a_copy, opts
39
+ a_copy
40
+ end
41
+
42
+ # Whether the id_token or access_token is missing or about to expire.
43
+ def needs_access_token?
44
+ send(token_type).nil? || expires_within?(60)
45
+ end
46
+
47
+ # Returns a reference to the #apply method, suitable for passing as
48
+ # a closure
49
+ def updater_proc
50
+ proc { |a_hash, opts = {}| apply a_hash, opts }
51
+ end
52
+
53
+ def on_refresh &block
54
+ @refresh_listeners = [] unless defined? @refresh_listeners
55
+ @refresh_listeners << block
56
+ end
57
+
58
+ def notify_refresh_listeners
59
+ listeners = defined?(@refresh_listeners) ? @refresh_listeners : []
60
+ listeners.each do |block|
61
+ block.call self
62
+ end
63
+ end
64
+
65
+ def expires_within?
66
+ raise NotImplementedError
67
+ end
68
+
69
+ private
70
+
71
+ def token_type
72
+ raise NotImplementedError
73
+ end
74
+
75
+ def fetch_access_token!
76
+ raise NotImplementedError
77
+ end
78
+ end
79
+ end
80
+ end
@@ -17,51 +17,67 @@ require "googleauth/credentials_loader"
17
17
 
18
18
  module Google
19
19
  module Auth
20
- # Representation of an application's identity for user authorization
21
- # flows.
20
+ ##
21
+ # Representation of an application's identity for user authorization flows.
22
+ #
22
23
  class ClientId
24
+ # Toplevel JSON key for the an installed app configuration.
25
+ # Must include client_id and client_secret subkeys if present.
23
26
  INSTALLED_APP = "installed".freeze
27
+ # Toplevel JSON key for the a webapp configuration.
28
+ # Must include client_id and client_secret subkeys if present.
24
29
  WEB_APP = "web".freeze
30
+ # JSON key for the client ID within an app configuration.
25
31
  CLIENT_ID = "client_id".freeze
32
+ # JSON key for the client secret within an app configuration.
26
33
  CLIENT_SECRET = "client_secret".freeze
34
+ # An error message raised when none of the expected toplevel properties
35
+ # can be found.
27
36
  MISSING_TOP_LEVEL_ELEMENT_ERROR =
28
37
  "Expected top level property 'installed' or 'web' to be present.".freeze
29
38
 
39
+ ##
30
40
  # Text identifier of the client ID
31
41
  # @return [String]
42
+ #
32
43
  attr_reader :id
33
44
 
45
+ ##
34
46
  # Secret associated with the client ID
35
47
  # @return [String]
48
+ #
36
49
  attr_reader :secret
37
50
 
38
51
  class << self
39
52
  attr_accessor :default
40
53
  end
41
54
 
42
- # Initialize the Client ID
55
+ ##
56
+ # Initialize the Client ID. Both id and secret must be non-nil.
43
57
  #
44
58
  # @param [String] id
45
59
  # Text identifier of the client ID
46
60
  # @param [String] secret
47
61
  # Secret associated with the client ID
48
- # @note Direction instantion is discouraged to avoid embedding IDs
49
- # & secrets in source. See {#from_file} to load from
62
+ # @note Direct instantiation is discouraged to avoid embedding IDs
63
+ # and secrets in source. See {#from_file} to load from
50
64
  # `client_secrets.json` files.
65
+ #
51
66
  def initialize id, secret
52
- CredentialsLoader.warn_if_cloud_sdk_credentials id
53
67
  raise "Client id can not be nil" if id.nil?
54
68
  raise "Client secret can not be nil" if secret.nil?
55
69
  @id = id
56
70
  @secret = secret
57
71
  end
58
72
 
73
+ ##
59
74
  # Constructs a Client ID from a JSON file downloaded from the
60
75
  # Google Developers Console.
61
76
  #
62
77
  # @param [String, File] file
63
78
  # Path of file to read from
64
79
  # @return [Google::Auth::ClientID]
80
+ #
65
81
  def self.from_file file
66
82
  raise "File can not be nil." if file.nil?
67
83
  File.open file.to_s do |f|
@@ -71,13 +87,14 @@ module Google
71
87
  end
72
88
  end
73
89
 
90
+ ##
74
91
  # Constructs a Client ID from a previously loaded JSON file. The hash
75
- # structure should
76
- # match the expected JSON format.
92
+ # structure should match the expected JSON format.
77
93
  #
78
94
  # @param [hash] config
79
95
  # Parsed contents of the JSON file
80
96
  # @return [Google::Auth::ClientID]
97
+ #
81
98
  def self.from_hash config
82
99
  raise "Hash can not be nil." if config.nil?
83
100
  raw_detail = config[INSTALLED_APP] || config[WEB_APP]
@@ -12,9 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require "faraday"
15
+ require "google-cloud-env"
16
16
  require "googleauth/signet"
17
- require "memoist"
18
17
 
19
18
  module Google
20
19
  # Module Auth provides classes that provide Google-specific authorization
@@ -34,74 +33,77 @@ module Google
34
33
  # Extends Signet::OAuth2::Client so that the auth token is obtained from
35
34
  # the GCE metadata server.
36
35
  class GCECredentials < Signet::OAuth2::Client
37
- # The IP Address is used in the URIs to speed up failures on non-GCE
38
- # systems.
36
+ # @private Unused and deprecated but retained to prevent breaking changes
39
37
  DEFAULT_METADATA_HOST = "169.254.169.254".freeze
40
38
 
41
- # @private Unused and deprecated
39
+ # @private Unused and deprecated but retained to prevent breaking changes
42
40
  COMPUTE_AUTH_TOKEN_URI =
43
41
  "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token".freeze
44
- # @private Unused and deprecated
42
+ # @private Unused and deprecated but retained to prevent breaking changes
45
43
  COMPUTE_ID_TOKEN_URI =
46
44
  "http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity".freeze
47
- # @private Unused and deprecated
45
+ # @private Unused and deprecated but retained to prevent breaking changes
48
46
  COMPUTE_CHECK_URI = "http://169.254.169.254".freeze
49
47
 
50
48
  class << self
51
- extend Memoist
52
-
49
+ # @private Unused and deprecated
53
50
  def metadata_host
54
51
  ENV.fetch "GCE_METADATA_HOST", DEFAULT_METADATA_HOST
55
52
  end
56
53
 
54
+ # @private Unused and deprecated
57
55
  def compute_check_uri
58
56
  "http://#{metadata_host}".freeze
59
57
  end
60
58
 
59
+ # @private Unused and deprecated
61
60
  def compute_auth_token_uri
62
61
  "#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/token".freeze
63
62
  end
64
63
 
64
+ # @private Unused and deprecated
65
65
  def compute_id_token_uri
66
66
  "#{compute_check_uri}/computeMetadata/v1/instance/service-accounts/default/identity".freeze
67
67
  end
68
68
 
69
69
  # Detect if this appear to be a GCE instance, by checking if metadata
70
70
  # is available.
71
- def on_gce? options = {}
72
- # TODO: This should use google-cloud-env instead.
73
- c = options[:connection] || Faraday.default_connection
74
- headers = { "Metadata-Flavor" => "Google" }
75
- resp = c.get compute_check_uri, nil, headers do |req|
76
- req.options.timeout = 1.0
77
- req.options.open_timeout = 0.1
78
- end
79
- return false unless resp.status == 200
80
- resp.headers["Metadata-Flavor"] == "Google"
81
- rescue Faraday::TimeoutError, Faraday::ConnectionFailed
82
- false
71
+ # The parameters are deprecated and unused.
72
+ def on_gce? _options = {}, _reload = false # rubocop:disable Style/OptionalBooleanParameter
73
+ Google::Cloud.env.metadata?
83
74
  end
84
75
 
85
- memoize :on_gce?
76
+ def reset_cache
77
+ Google::Cloud.env.compute_metadata.reset_existence!
78
+ Google::Cloud.env.compute_metadata.cache.expire_all!
79
+ end
80
+ alias unmemoize_all reset_cache
81
+ end
82
+
83
+ # Construct a GCECredentials
84
+ def initialize options = {}
85
+ # Override the constructor to remember whether the universe domain was
86
+ # overridden by a constructor argument.
87
+ @universe_domain_overridden = options["universe_domain"] || options[:universe_domain] ? true : false
88
+ super options
86
89
  end
87
90
 
88
91
  # Overrides the super class method to change how access tokens are
89
92
  # fetched.
90
- def fetch_access_token options = {}
91
- c = options[:connection] || Faraday.default_connection
92
- retry_with_error do
93
- uri = target_audience ? GCECredentials.compute_id_token_uri : GCECredentials.compute_auth_token_uri
94
- query = target_audience ? { "audience" => target_audience, "format" => "full" } : {}
95
- query[:scopes] = Array(scope).join "," if scope
96
- resp = c.get uri, query, "Metadata-Flavor" => "Google"
93
+ def fetch_access_token _options = {}
94
+ if token_type == :id_token
95
+ query = { "audience" => target_audience, "format" => "full" }
96
+ entry = "service-accounts/default/identity"
97
+ else
98
+ query = {}
99
+ entry = "service-accounts/default/token"
100
+ end
101
+ query[:scopes] = Array(scope).join "," if scope
102
+ begin
103
+ resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
97
104
  case resp.status
98
105
  when 200
99
- content_type = resp.headers["content-type"]
100
- if ["text/html", "application/text"].include? content_type
101
- { (target_audience ? "id_token" : "access_token") => resp.body }
102
- else
103
- Signet::OAuth2.parse_credentials resp.body, content_type
104
- end
106
+ build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
105
107
  when 403, 500
106
108
  msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
107
109
  raise Signet::UnexpectedStatusError, msg
@@ -111,7 +113,35 @@ module Google
111
113
  msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
112
114
  raise Signet::AuthorizationError, msg
113
115
  end
116
+ rescue Google::Cloud::Env::MetadataServerNotResponding => e
117
+ raise Signet::AuthorizationError, e.message
118
+ end
119
+ end
120
+
121
+ private
122
+
123
+ def build_token_hash body, content_type, retrieval_time
124
+ hash =
125
+ if ["text/html", "application/text"].include? content_type
126
+ { token_type.to_s => body }
127
+ else
128
+ Signet::OAuth2.parse_credentials body, content_type
129
+ end
130
+ unless @universe_domain_overridden
131
+ universe_domain = Google::Cloud.env.lookup_metadata "universe", "universe_domain"
132
+ universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty?
133
+ hash["universe_domain"] = universe_domain.strip
134
+ end
135
+ # The response might have been cached, which means expires_in might be
136
+ # stale. Update it based on the time since the data was retrieved.
137
+ # We also ensure expires_in is conservative; subtracting at least 1
138
+ # second to offset any skew from metadata server latency.
139
+ if hash["expires_in"].is_a? Numeric
140
+ offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round
141
+ hash["expires_in"] -= offset if offset.positive?
142
+ hash["expires_in"] = 0 if hash["expires_in"].negative?
114
143
  end
144
+ hash
115
145
  end
116
146
  end
117
147
  end
@@ -259,7 +259,7 @@ module Google
259
259
  # @return [Object] The value
260
260
  #
261
261
  def self.lookup_auth_param name, method_name = name
262
- val = instance_variable_get "@#{name}".to_sym
262
+ val = instance_variable_get :"@#{name}"
263
263
  val = yield if val.nil? && block_given?
264
264
  return val unless val.nil?
265
265
  return superclass.send method_name if superclass.respond_to? method_name
@@ -328,9 +328,13 @@ module Google
328
328
  # @return [Proc] Returns a reference to the {Signet::OAuth2::Client#apply} method,
329
329
  # suitable for passing as a closure.
330
330
  #
331
+ # @!attribute [rw] universe_domain
332
+ # @return [String] The universe domain issuing these credentials.
333
+ #
331
334
  def_delegators :@client,
332
335
  :token_credential_uri, :audience,
333
- :scope, :issuer, :signing_key, :updater_proc, :target_audience
336
+ :scope, :issuer, :signing_key, :updater_proc, :target_audience,
337
+ :universe_domain, :universe_domain=
334
338
 
335
339
  ##
336
340
  # Creates a new Credentials instance with the provided auth credentials, and with the default
@@ -352,17 +356,17 @@ module Google
352
356
  #
353
357
  def initialize keyfile, options = {}
354
358
  verify_keyfile_provided! keyfile
355
- @project_id = options["project_id"] || options["project"]
356
- @quota_project_id = options["quota_project_id"]
359
+ options = symbolize_hash_keys options
360
+ @project_id = options[:project_id] || options[:project]
361
+ @quota_project_id = options[:quota_project_id]
357
362
  case keyfile
358
- when Signet::OAuth2::Client
363
+ when Google::Auth::BaseClient
359
364
  update_from_signet keyfile
360
365
  when Hash
361
366
  update_from_hash keyfile, options
362
367
  else
363
368
  update_from_filepath keyfile, options
364
369
  end
365
- CredentialsLoader.warn_if_cloud_sdk_credentials @client.client_id
366
370
  @project_id ||= CredentialsLoader.load_gcloud_project_id
367
371
  @client.fetch_access_token! if @client.needs_access_token?
368
372
  @env_vars = nil
@@ -481,10 +485,11 @@ module Google
481
485
  end
482
486
 
483
487
  # Initializes the Signet client.
484
- def init_client keyfile, connection_options = {}
485
- client_opts = client_options keyfile
486
- Signet::OAuth2::Client.new(client_opts)
487
- .configure_connection(connection_options)
488
+ def init_client hash, options = {}
489
+ options = update_client_options options
490
+ io = StringIO.new JSON.generate hash
491
+ options.merge! json_key_io: io
492
+ Google::Auth::DefaultCredentials.make_creds options
488
493
  end
489
494
 
490
495
  # returns a new Hash with string keys instead of symbol keys.
@@ -492,31 +497,28 @@ module Google
492
497
  hash.to_h.transform_keys(&:to_s)
493
498
  end
494
499
 
495
- # rubocop:disable Metrics/AbcSize
500
+ # returns a new Hash with symbol keys instead of string keys.
501
+ def symbolize_hash_keys hash
502
+ hash.to_h.transform_keys(&:to_sym)
503
+ end
504
+
505
+ def update_client_options options
506
+ options = options.dup
496
507
 
497
- def client_options options
498
- # Keyfile options have higher priority over constructor defaults
499
- options["token_credential_uri"] ||= self.class.token_credential_uri
500
- options["audience"] ||= self.class.audience
501
- options["scope"] ||= self.class.scope
502
- options["target_audience"] ||= self.class.target_audience
508
+ # options have higher priority over constructor defaults
509
+ options[:token_credential_uri] ||= self.class.token_credential_uri
510
+ options[:audience] ||= self.class.audience
511
+ options[:scope] ||= self.class.scope
512
+ options[:target_audience] ||= self.class.target_audience
503
513
 
504
- if !Array(options["scope"]).empty? && options["target_audience"]
514
+ if !Array(options[:scope]).empty? && options[:target_audience]
505
515
  raise ArgumentError, "Cannot specify both scope and target_audience"
506
516
  end
517
+ options.delete :scope unless options[:target_audience].nil?
507
518
 
508
- needs_scope = options["target_audience"].nil?
509
- # client options for initializing signet client
510
- { token_credential_uri: options["token_credential_uri"],
511
- audience: options["audience"],
512
- scope: (needs_scope ? Array(options["scope"]) : nil),
513
- target_audience: options["target_audience"],
514
- issuer: options["client_email"],
515
- signing_key: OpenSSL::PKey::RSA.new(options["private_key"]) }
519
+ options
516
520
  end
517
521
 
518
- # rubocop:enable Metrics/AbcSize
519
-
520
522
  def update_from_signet client
521
523
  @project_id ||= client.project_id if client.respond_to? :project_id
522
524
  @quota_project_id ||= client.quota_project_id if client.respond_to? :quota_project_id
@@ -527,7 +529,7 @@ module Google
527
529
  hash = stringify_hash_keys hash
528
530
  hash["scope"] ||= options[:scope]
529
531
  hash["target_audience"] ||= options[:target_audience]
530
- @project_id ||= (hash["project_id"] || hash["project"])
532
+ @project_id ||= hash["project_id"] || hash["project"]
531
533
  @quota_project_id ||= hash["quota_project_id"]
532
534
  @client = init_client hash, options
533
535
  end
@@ -537,7 +539,7 @@ module Google
537
539
  json = JSON.parse ::File.read(path)
538
540
  json["scope"] ||= options[:scope]
539
541
  json["target_audience"] ||= options[:target_audience]
540
- @project_id ||= (json["project_id"] || json["project"])
542
+ @project_id ||= json["project_id"] || json["project"]
541
543
  @quota_project_id ||= json["quota_project_id"]
542
544
  @client = init_client json, options
543
545
  end