googleauth 1.3.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.
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