googleauth 1.11.1 → 1.12.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: d69318fbe3400719c053c16d4de765ae8a87ad1250c6f62c2c1c2e9c51a97d89
4
- data.tar.gz: be832e5bfb8417794534ba11186a6175a2e0e28b06c702c095330f2818345973
3
+ metadata.gz: 7f0abc7ed9fc9deefc0b45abbd3b66d6b4a5aa859774f652e2b8c8c8ff12ff77
4
+ data.tar.gz: c8a2ab192e92bf610f96044c04d5e02ad138dd99f2a1311d14d4e029639b210e
5
5
  SHA512:
6
- metadata.gz: cb7ac3b1d73c1375434d0bfa14ed2e83a2b5fd4f2a5c917a656ff12bdf91028b62724ef9b3774092ac377941b7a6596828a4a87d1ea7e00d1e82a57893981505
7
- data.tar.gz: 8f00b43299f1dcc2989b256fbc917f12f0df9ee1b4a000752225a834a8d5a360aea2bf58cb80ec5888e114f14a0e7cd9cae8958be8ebde146c21953b12a74c6d
6
+ metadata.gz: 17bde72c9466591719a1f069875242a6abbfa9533fef63d5c9d696c16227d68ac2fd053d63f9d46588cb23c38841eb04a27953a38bbd63c2bc8f8f02709f684c
7
+ data.tar.gz: 4e4c81aad747f3beb7fd610e836ef871e5fa03a2ab7e9b6c2b223cd48e5e891976e736ad90539a493de8ce2805c692667bf3f11162ecb8646ba7a59be82eface
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Release History
2
2
 
3
+ ### 1.12.0 (2024-12-05)
4
+
5
+ #### Features
6
+
7
+ * provided opt-in debug logging ([#490](https://github.com/googleapis/google-auth-library-ruby/issues/490))
8
+
9
+ ### 1.11.2 (2024-10-23)
10
+
11
+ #### Bug Fixes
12
+
13
+ * Temporarily disable universe domain query from GCE metadata server ([#493](https://github.com/googleapis/google-auth-library-ruby/issues/493))
14
+ * Use updated metadata path for universe-domain ([#496](https://github.com/googleapis/google-auth-library-ruby/issues/496))
15
+
3
16
  ### 1.11.1 (2024-10-04)
4
17
 
5
18
  #### Bug Fixes
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "google/logging/message"
16
+
15
17
  module Google
16
18
  # Module Auth provides classes that provide Google-specific authorization
17
19
  # used to access Google APIs.
@@ -29,7 +31,12 @@ module Google
29
31
  # fetch the access token there is currently not one, or if the client
30
32
  # has expired
31
33
  fetch_access_token! opts if needs_access_token?
32
- a_hash[AUTH_METADATA_KEY] = "Bearer #{send token_type}"
34
+ token = send token_type
35
+ a_hash[AUTH_METADATA_KEY] = "Bearer #{token}"
36
+ logger&.debug do
37
+ hash = Digest::SHA256.hexdigest token
38
+ Google::Logging::Message.from message: "Sending auth token. (sha256:#{hash})"
39
+ end
33
40
  end
34
41
 
35
42
  # Returns a clone of a_hash updated with the authentication token
@@ -66,6 +73,9 @@ module Google
66
73
  raise NoMethodError, "expires_within? not implemented"
67
74
  end
68
75
 
76
+ # The logger used to log operations on this client, such as token refresh.
77
+ attr_accessor :logger
78
+
69
79
  private
70
80
 
71
81
  def token_type
@@ -80,46 +80,87 @@ module Google
80
80
  alias unmemoize_all reset_cache
81
81
  end
82
82
 
83
+ # @private Temporary; remove when universe domain metadata endpoint is stable (see b/349488459).
84
+ attr_accessor :disable_universe_domain_check
85
+
83
86
  # Construct a GCECredentials
84
87
  def initialize options = {}
85
88
  # Override the constructor to remember whether the universe domain was
86
89
  # overridden by a constructor argument.
87
90
  @universe_domain_overridden = options["universe_domain"] || options[:universe_domain] ? true : false
91
+ # TODO: Remove when universe domain metadata endpoint is stable (see b/349488459).
92
+ @disable_universe_domain_check = true
88
93
  super options
89
94
  end
90
95
 
91
96
  # Overrides the super class method to change how access tokens are
92
97
  # fetched.
93
98
  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
99
+ query, entry =
100
+ if token_type == :id_token
101
+ [{ "audience" => target_audience, "format" => "full" }, "service-accounts/default/identity"]
102
+ else
103
+ [{}, "service-accounts/default/token"]
104
+ end
101
105
  query[:scopes] = Array(scope).join "," if scope
102
106
  begin
107
+ log_fetch_query
103
108
  resp = Google::Cloud.env.lookup_metadata_response "instance", entry, query: query
109
+ log_fetch_resp resp
104
110
  case resp.status
105
111
  when 200
106
112
  build_token_hash resp.body, resp.headers["content-type"], resp.retrieval_monotonic_time
107
113
  when 403, 500
108
- msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
109
- raise Signet::UnexpectedStatusError, msg
114
+ raise Signet::UnexpectedStatusError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
110
115
  when 404
111
116
  raise Signet::AuthorizationError, NO_METADATA_SERVER_ERROR
112
117
  else
113
- msg = "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
114
- raise Signet::AuthorizationError, msg
118
+ raise Signet::AuthorizationError, "Unexpected error code #{resp.status} #{UNEXPECTED_ERROR_SUFFIX}"
115
119
  end
116
120
  rescue Google::Cloud::Env::MetadataServerNotResponding => e
121
+ log_fetch_err e
117
122
  raise Signet::AuthorizationError, e.message
118
123
  end
119
124
  end
120
125
 
121
126
  private
122
127
 
128
+ def log_fetch_query
129
+ if token_type == :id_token
130
+ logger&.info do
131
+ Google::Logging::Message.from(
132
+ message: "Requesting id token from MDS with aud=#{target_audience}",
133
+ "credentialsId" => object_id
134
+ )
135
+ end
136
+ else
137
+ logger&.info do
138
+ Google::Logging::Message.from(
139
+ message: "Requesting access token from MDS",
140
+ "credentialsId" => object_id
141
+ )
142
+ end
143
+ end
144
+ end
145
+
146
+ def log_fetch_resp resp
147
+ logger&.info do
148
+ Google::Logging::Message.from(
149
+ message: "Received #{resp.status} from MDS",
150
+ "credentialsId" => object_id
151
+ )
152
+ end
153
+ end
154
+
155
+ def log_fetch_err _err
156
+ logger&.info do
157
+ Google::Logging::Message.from(
158
+ message: "MDS did not respond to token request",
159
+ "credentialsId" => object_id
160
+ )
161
+ end
162
+ end
163
+
123
164
  def build_token_hash body, content_type, retrieval_time
124
165
  hash =
125
166
  if ["text/html", "application/text"].include? content_type
@@ -127,20 +168,8 @@ module Google
127
168
  else
128
169
  Signet::OAuth2.parse_credentials body, content_type
129
170
  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?
143
- end
171
+ add_universe_domain_to hash
172
+ adjust_for_stale_expires_in hash, retrieval_time
144
173
  hash
145
174
  end
146
175
 
@@ -152,6 +181,30 @@ module Google
152
181
  end
153
182
  hash
154
183
  end
184
+
185
+ def add_universe_domain_to hash
186
+ return if @universe_domain_overridden
187
+ universe_domain =
188
+ if disable_universe_domain_check
189
+ # TODO: Remove when universe domain metadata endpoint is stable (see b/349488459).
190
+ "googleapis.com"
191
+ else
192
+ Google::Cloud.env.lookup_metadata "universe", "universe-domain"
193
+ end
194
+ universe_domain = "googleapis.com" if !universe_domain || universe_domain.empty?
195
+ hash["universe_domain"] = universe_domain.strip
196
+ end
197
+
198
+ # The response might have been cached, which means expires_in might be
199
+ # stale. Update it based on the time since the data was retrieved.
200
+ # We also ensure expires_in is conservative; subtracting at least 1
201
+ # second to offset any skew from metadata server latency.
202
+ def adjust_for_stale_expires_in hash, retrieval_time
203
+ return unless hash["expires_in"].is_a? Numeric
204
+ offset = 1 + (Process.clock_gettime(Process::CLOCK_MONOTONIC) - retrieval_time).round
205
+ hash["expires_in"] -= offset if offset.positive?
206
+ hash["expires_in"] = 0 if hash["expires_in"].negative?
207
+ end
155
208
  end
156
209
  end
157
210
  end
@@ -299,6 +299,12 @@ module Google
299
299
  #
300
300
  attr_reader :quota_project_id
301
301
 
302
+ # @private Temporary; remove when universe domain metadata endpoint is stable (see b/349488459).
303
+ def disable_universe_domain_check
304
+ return false unless @client.respond_to? :disable_universe_domain_check
305
+ @client.disable_universe_domain_check
306
+ end
307
+
302
308
  # @private Delegate client methods to the client object.
303
309
  extend Forwardable
304
310
 
@@ -331,10 +337,13 @@ module Google
331
337
  # @!attribute [rw] universe_domain
332
338
  # @return [String] The universe domain issuing these credentials.
333
339
  #
340
+ # @!attribute [rw] logger
341
+ # @return [Logger] The logger used to log credential operations such as token refresh.
342
+ #
334
343
  def_delegators :@client,
335
344
  :token_credential_uri, :audience,
336
345
  :scope, :issuer, :signing_key, :updater_proc, :target_audience,
337
- :universe_domain, :universe_domain=
346
+ :universe_domain, :universe_domain=, :logger, :logger=
338
347
 
339
348
  ##
340
349
  # Creates a new Credentials instance with the provided auth credentials, and with the default
@@ -343,16 +352,17 @@ module Google
343
352
  # @param [String, Hash, Signet::OAuth2::Client] keyfile
344
353
  # The keyfile can be provided as one of the following:
345
354
  #
346
- # * The path to a JSON keyfile (as a +String+)
347
- # * The contents of a JSON keyfile (as a +Hash+)
348
- # * A +Signet::OAuth2::Client+ object
355
+ # * The path to a JSON keyfile (as a `String`)
356
+ # * The contents of a JSON keyfile (as a `Hash`)
357
+ # * A `Signet::OAuth2::Client` object
349
358
  # @param [Hash] options
350
359
  # The options for configuring the credentials instance. The following is supported:
351
360
  #
352
- # * +:scope+ - the scope for the client
353
- # * +"project_id"+ (and optionally +"project"+) - the project identifier for the client
354
- # * +:connection_builder+ - the connection builder to use for the client
355
- # * +:default_connection+ - the default connection to use for the client
361
+ # * `:scope` - the scope for the client
362
+ # * `project_id` (and optionally `project`) - the project identifier for the client
363
+ # * `:connection_builder` - the connection builder to use for the client
364
+ # * `:default_connection` - the default connection to use for the client
365
+ # * `:logger` - the logger used to log credential operations such as token refresh.
356
366
  #
357
367
  def initialize keyfile, options = {}
358
368
  verify_keyfile_provided! keyfile
@@ -367,8 +377,8 @@ module Google
367
377
  else
368
378
  update_from_filepath keyfile, options
369
379
  end
380
+ setup_logging logger: options.fetch(:logger, :default)
370
381
  @project_id ||= CredentialsLoader.load_gcloud_project_id
371
- @client.fetch_access_token! if @client.needs_access_token?
372
382
  @env_vars = nil
373
383
  @paths = nil
374
384
  @scope = nil
@@ -462,7 +472,8 @@ module Google
462
472
  audience: options[:audience] || audience
463
473
  }
464
474
  client = Google::Auth::DefaultCredentials.make_creds creds_input
465
- new client
475
+ options = options.select { |k, _v| k == :logger }
476
+ new client, options
466
477
  end
467
478
 
468
479
  private_class_method :from_env_vars,
@@ -543,6 +554,23 @@ module Google
543
554
  @quota_project_id ||= json["quota_project_id"]
544
555
  @client = init_client json, options
545
556
  end
557
+
558
+ def setup_logging logger: :default
559
+ return unless @client.respond_to? :logger=
560
+ logging_env = ENV["GOOGLE_SDK_RUBY_LOGGING_GEMS"].to_s.downcase
561
+ if ["false", "none"].include? logging_env
562
+ logger = nil
563
+ elsif @client.logger
564
+ logger = @client.logger
565
+ elsif logger == :default
566
+ logger = nil
567
+ if ["true", "all"].include?(logging_env) || logging_env.split(",").include?("googleauth")
568
+ formatter = Google::Logging::StructuredFormatter.new if Google::Cloud::Env.get.logging_agent_expected?
569
+ logger = Logger.new $stderr, progname: "googleauth", formatter: formatter
570
+ end
571
+ end
572
+ @client.logger = logger
573
+ end
546
574
  end
547
575
  end
548
576
  end
@@ -129,7 +129,7 @@ module Google
129
129
  if @client_id.nil? && @workforce_pool_user_project
130
130
  additional_options = { userProject: @workforce_pool_user_project }
131
131
  end
132
- @sts_client.exchange_token(
132
+ token_request = {
133
133
  audience: @audience,
134
134
  grant_type: STS_GRANT_TYPE,
135
135
  subject_token: retrieve_subject_token!,
@@ -137,10 +137,31 @@ module Google
137
137
  scopes: @service_account_impersonation_url ? IAM_SCOPE : @scope,
138
138
  requested_token_type: STS_REQUESTED_TOKEN_TYPE,
139
139
  additional_options: additional_options
140
- )
140
+ }
141
+ log_token_request token_request
142
+ @sts_client.exchange_token token_request
143
+ end
144
+
145
+ def log_token_request token_request
146
+ logger&.info do
147
+ Google::Logging::Message.from(
148
+ message: "Requesting access token from #{token_request[:grant_type]}",
149
+ "credentialsId" => object_id
150
+ )
151
+ end
152
+ logger&.debug do
153
+ digest = Digest::SHA256.hexdigest token_request[:subject_token].to_s
154
+ loggable_request = token_request.merge subject_token: "(sha256:#{digest})"
155
+ Google::Logging::Message.from(
156
+ message: "Request data",
157
+ "request" => loggable_request,
158
+ "credentialsId" => object_id
159
+ )
160
+ end
141
161
  end
142
162
 
143
163
  def get_impersonated_access_token token, _options = {}
164
+ log_impersonated_token_request token
144
165
  response = connection.post @service_account_impersonation_url do |req|
145
166
  req.headers["Authorization"] = "Bearer #{token}"
146
167
  req.headers["Content-Type"] = "application/json"
@@ -153,6 +174,16 @@ module Google
153
174
 
154
175
  MultiJson.load response.body
155
176
  end
177
+
178
+ def log_impersonated_token_request original_token
179
+ logger&.info do
180
+ digest = Digest::SHA256.hexdigest original_token
181
+ Google::Logging::Message.from(
182
+ message: "Requesting impersonated access token with original token (sha256:#{digest})",
183
+ "credentialsId" => object_id
184
+ )
185
+ end
186
+ end
156
187
  end
157
188
  end
158
189
  end
@@ -168,7 +168,6 @@ module Google
168
168
  aud: nil,
169
169
  azp: nil,
170
170
  iss: OIDC_ISSUERS
171
-
172
171
  verifier = Verifier.new key_source: oidc_key_source,
173
172
  aud: aud,
174
173
  azp: azp,
@@ -206,7 +205,6 @@ module Google
206
205
  aud: nil,
207
206
  azp: nil,
208
207
  iss: IAP_ISSUERS
209
-
210
208
  verifier = Verifier.new key_source: iap_key_source,
211
209
  aud: aud,
212
210
  azp: azp,
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "google/logging/message"
15
16
  require "googleauth/signet"
16
17
  require "googleauth/credentials_loader"
17
18
  require "googleauth/json_key_reader"
@@ -123,6 +124,7 @@ module Google
123
124
  }
124
125
  key_io = StringIO.new MultiJson.dump(cred_json)
125
126
  alt = ServiceAccountJwtHeaderCredentials.make_creds json_key_io: key_io, scope: scope
127
+ alt.logger = logger
126
128
  alt.apply! a_hash
127
129
  end
128
130
  end
@@ -147,6 +149,7 @@ module Google
147
149
  attr_reader :project_id
148
150
  attr_reader :quota_project_id
149
151
  attr_accessor :universe_domain
152
+ attr_accessor :logger
150
153
 
151
154
  # Create a ServiceAccountJwtHeaderCredentials.
152
155
  #
@@ -187,10 +190,14 @@ module Google
187
190
  return a_hash if jwt_aud_uri.nil? && @scope.nil?
188
191
  jwt_token = new_jwt_token jwt_aud_uri, opts
189
192
  a_hash[AUTH_METADATA_KEY] = "Bearer #{jwt_token}"
193
+ logger&.debug do
194
+ hash = Digest::SHA256.hexdigest jwt_token
195
+ Google::Logging::Message.from message: "Sending JWT auth token. (sha256:#{hash})"
196
+ end
190
197
  a_hash
191
198
  end
192
199
 
193
- # Returns a clone of a_hash updated with the authoriation header
200
+ # Returns a clone of a_hash updated with the authorization header
194
201
  def apply a_hash, opts = {}
195
202
  a_copy = a_hash.clone
196
203
  apply! a_copy, opts
@@ -219,6 +226,10 @@ module Google
219
226
  assertion["scope"] = Array(@scope).join " " if @scope
220
227
  assertion["aud"] = jwt_aud_uri if jwt_aud_uri
221
228
 
229
+ logger&.debug do
230
+ Google::Logging::Message.from message: "JWT assertion: #{assertion}"
231
+ end
232
+
222
233
  JWT.encode assertion, @signing_key, SIGNING_ALGORITHM
223
234
  end
224
235
 
@@ -65,6 +65,24 @@ module Signet
65
65
  info
66
66
  end
67
67
 
68
+ alias googleauth_orig_generate_access_token_request generate_access_token_request
69
+ def generate_access_token_request options = {}
70
+ parameters = googleauth_orig_generate_access_token_request options
71
+ logger&.info do
72
+ Google::Logging::Message.from(
73
+ message: "Requesting access token from #{parameters['grant_type']}",
74
+ "credentialsId" => object_id
75
+ )
76
+ end
77
+ logger&.debug do
78
+ Google::Logging::Message.from(
79
+ message: "Token fetch params: #{parameters}",
80
+ "credentialsId" => object_id
81
+ )
82
+ end
83
+ parameters
84
+ end
85
+
68
86
  def build_default_connection
69
87
  if !defined?(@connection_info)
70
88
  nil
@@ -79,15 +97,20 @@ module Signet
79
97
  retry_count = 0
80
98
 
81
99
  begin
82
- yield
100
+ yield.tap { |resp| log_response resp }
83
101
  rescue StandardError => e
84
- raise e if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
102
+ if e.is_a?(Signet::AuthorizationError) || e.is_a?(Signet::ParseError)
103
+ log_auth_error e
104
+ raise e
105
+ end
85
106
 
86
107
  if retry_count < max_retry_count
108
+ log_transient_error e
87
109
  retry_count += 1
88
110
  sleep retry_count * 0.3
89
111
  retry
90
112
  else
113
+ log_retries_exhausted e
91
114
  msg = "Unexpected error: #{e.inspect}"
92
115
  raise Signet::AuthorizationError, msg
93
116
  end
@@ -106,6 +129,49 @@ module Signet
106
129
  # Shouldn't happen unless we get a garbled ID token
107
130
  nil
108
131
  end
132
+
133
+ def log_response token_response
134
+ response_hash = JSON.parse token_response rescue {}
135
+ if response_hash["access_token"]
136
+ digest = Digest::SHA256.hexdigest response_hash["access_token"]
137
+ response_hash["access_token"] = "(sha256:#{digest})"
138
+ end
139
+ if response_hash["id_token"]
140
+ digest = Digest::SHA256.hexdigest response_hash["id_token"]
141
+ response_hash["id_token"] = "(sha256:#{digest})"
142
+ end
143
+ Google::Logging::Message.from(
144
+ message: "Received auth token response: #{response_hash}",
145
+ "credentialsId" => object_id
146
+ )
147
+ end
148
+
149
+ def log_auth_error err
150
+ logger&.info do
151
+ Google::Logging::Message.from(
152
+ message: "Auth error when fetching auth token: #{err}",
153
+ "credentialsId" => object_id
154
+ )
155
+ end
156
+ end
157
+
158
+ def log_transient_error err
159
+ logger&.info do
160
+ Google::Logging::Message.from(
161
+ message: "Transient error when fetching auth token: #{err}",
162
+ "credentialsId" => object_id
163
+ )
164
+ end
165
+ end
166
+
167
+ def log_retries_exhausted err
168
+ logger&.info do
169
+ Google::Logging::Message.from(
170
+ message: "Exhausted retries when fetching auth token: #{err}",
171
+ "credentialsId" => object_id
172
+ )
173
+ end
174
+ end
109
175
  end
110
176
  end
111
177
  end
@@ -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.11.1".freeze
19
+ VERSION = "1.12.0".freeze
20
20
  end
21
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googleauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Emiola
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-04 00:00:00.000000000 Z
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -36,14 +36,28 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.1'
39
+ version: '2.2'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.1'
46
+ version: '2.2'
47
+ - !ruby/object:Gem::Dependency
48
+ name: google-logging-utils
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.1'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.1'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: jwt
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -186,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
200
  - !ruby/object:Gem::Version
187
201
  version: '0'
188
202
  requirements: []
189
- rubygems_version: 3.5.6
203
+ rubygems_version: 3.5.23
190
204
  signing_key:
191
205
  specification_version: 4
192
206
  summary: Google Auth Library for Ruby