googleauth 1.11.1 → 1.12.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: 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