kubeclient 4.2.2 → 4.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kubeclient might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.travis.yml +22 -6
- data/CHANGELOG.md +46 -0
- data/README.md +258 -65
- data/RELEASING.md +4 -1
- data/kubeclient.gemspec +6 -3
- data/lib/kubeclient.rb +4 -2
- data/lib/kubeclient/aws_eks_credentials.rb +46 -0
- data/lib/kubeclient/common.rb +49 -17
- data/lib/kubeclient/config.rb +20 -2
- data/lib/kubeclient/gcp_auth_provider.rb +19 -0
- data/lib/kubeclient/gcp_command_credentials.rb +31 -0
- data/lib/kubeclient/google_application_default_credentials.rb +17 -2
- data/lib/kubeclient/oidc_auth_provider.rb +52 -0
- data/lib/kubeclient/version.rb +1 -1
- data/test/config/gcpauth.kubeconfig +22 -0
- data/test/config/gcpcmdauth.kubeconfig +26 -0
- data/test/config/oidcauth.kubeconfig +25 -0
- data/test/json/service_json_patch.json +26 -0
- data/test/json/service_merge_patch.json +26 -0
- data/test/test_config.rb +51 -0
- data/test/test_gcp_command_credentials.rb +27 -0
- data/test/test_helper.rb +1 -0
- data/test/test_kubeclient.rb +16 -0
- data/test/test_oidc_auth_provider.rb +103 -0
- data/test/test_pod_log.rb +37 -3
- data/test/test_service.rb +54 -0
- data/test/test_watch.rb +13 -0
- metadata +77 -12
data/RELEASING.md
CHANGED
@@ -20,7 +20,10 @@ Edit `CHANGELOG.md` as necessary. Even if all included changes remembered to up
|
|
20
20
|
|
21
21
|
Bump `lib/kubeclient/version.rb` manually, or by using:
|
22
22
|
```bash
|
23
|
-
|
23
|
+
RELEASE_VERSION=x.y.z
|
24
|
+
|
25
|
+
git checkout -b "release-$RELEASE_VERSION" $RELEASE_BRANCH
|
26
|
+
# Won't work with uncommitted changes, you have to commit the changelog first.
|
24
27
|
gem bump --version $RELEASE_VERSION
|
25
28
|
git show # View version bump change.
|
26
29
|
```
|
data/kubeclient.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
spec.required_ruby_version = '>= 2.2.0'
|
22
22
|
|
23
|
-
spec.add_development_dependency 'bundler', '
|
23
|
+
spec.add_development_dependency 'bundler', '>= 1.6'
|
24
24
|
spec.add_development_dependency 'rake', '~> 12.0'
|
25
25
|
spec.add_development_dependency 'minitest'
|
26
26
|
spec.add_development_dependency 'minitest-rg'
|
@@ -28,8 +28,11 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'vcr'
|
29
29
|
spec.add_development_dependency 'rubocop', '= 0.49.1'
|
30
30
|
spec.add_development_dependency 'googleauth', '~> 0.5.1'
|
31
|
+
spec.add_development_dependency('mocha', '~> 1.5')
|
32
|
+
spec.add_development_dependency 'openid_connect', '~> 1.1'
|
33
|
+
spec.add_development_dependency 'jsonpath', '~> 1.0'
|
31
34
|
|
32
35
|
spec.add_dependency 'rest-client', '~> 2.0'
|
33
|
-
spec.add_dependency 'recursive-open-struct', '~> 1.
|
34
|
-
spec.add_dependency 'http', '
|
36
|
+
spec.add_dependency 'recursive-open-struct', '~> 1.1', '>= 1.1.1'
|
37
|
+
spec.add_dependency 'http', '>= 3.0', '< 5.0'
|
35
38
|
end
|
data/lib/kubeclient.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'rest-client'
|
3
3
|
|
4
|
+
require 'kubeclient/aws_eks_credentials'
|
4
5
|
require 'kubeclient/common'
|
5
6
|
require 'kubeclient/config'
|
6
7
|
require 'kubeclient/entity_list'
|
7
|
-
require 'kubeclient/google_application_default_credentials'
|
8
8
|
require 'kubeclient/exec_credentials'
|
9
|
+
require 'kubeclient/gcp_auth_provider'
|
9
10
|
require 'kubeclient/http_error'
|
10
11
|
require 'kubeclient/missing_kind_compatibility'
|
12
|
+
require 'kubeclient/oidc_auth_provider'
|
11
13
|
require 'kubeclient/resource'
|
12
14
|
require 'kubeclient/resource_not_found_error'
|
13
15
|
require 'kubeclient/version'
|
@@ -26,7 +28,7 @@ module Kubeclient
|
|
26
28
|
uri,
|
27
29
|
'/api',
|
28
30
|
version,
|
29
|
-
options
|
31
|
+
**options
|
30
32
|
)
|
31
33
|
end
|
32
34
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kubeclient
|
4
|
+
# Get a bearer token to authenticate against aws eks.
|
5
|
+
class AmazonEksCredentials
|
6
|
+
class AmazonEksDependencyError < LoadError # rubocop:disable Lint/InheritException
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def token(credentials, eks_cluster)
|
11
|
+
begin
|
12
|
+
require 'aws-sigv4'
|
13
|
+
require 'base64'
|
14
|
+
require 'cgi'
|
15
|
+
rescue LoadError => e
|
16
|
+
raise AmazonEksDependencyError,
|
17
|
+
'Error requiring aws gems. Kubeclient itself does not include the following ' \
|
18
|
+
'gems: [aws-sigv4]. To support auth-provider eks, you must ' \
|
19
|
+
"include it in your calling application. Failed with: #{e.message}"
|
20
|
+
end
|
21
|
+
# https://github.com/aws/aws-sdk-ruby/pull/1848
|
22
|
+
# Get a signer
|
23
|
+
# Note - sts only has ONE endpoint (not regional) so 'us-east-1' hardcoding should be OK
|
24
|
+
signer = Aws::Sigv4::Signer.new(
|
25
|
+
service: 'sts',
|
26
|
+
region: 'us-east-1',
|
27
|
+
credentials: credentials
|
28
|
+
)
|
29
|
+
|
30
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Sigv4/Signer.html#presign_url-instance_method
|
31
|
+
presigned_url_string = signer.presign_url(
|
32
|
+
http_method: 'GET',
|
33
|
+
url: 'https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15',
|
34
|
+
body: '',
|
35
|
+
credentials: credentials,
|
36
|
+
expires_in: 60,
|
37
|
+
headers: {
|
38
|
+
'X-K8s-Aws-Id' => eks_cluster
|
39
|
+
}
|
40
|
+
)
|
41
|
+
kube_token = 'k8s-aws-v1.' + Base64.urlsafe_encode64(presigned_url_string.to_s).sub(/=*$/, '') # rubocop:disable Metrics/LineLength
|
42
|
+
kube_token
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/kubeclient/common.rb
CHANGED
@@ -5,7 +5,7 @@ module Kubeclient
|
|
5
5
|
# Common methods
|
6
6
|
# this is mixed in by other gems
|
7
7
|
module ClientMixin
|
8
|
-
ENTITY_METHODS = %w[get watch delete create update patch].freeze
|
8
|
+
ENTITY_METHODS = %w[get watch delete create update patch json_patch merge_patch].freeze
|
9
9
|
|
10
10
|
DEFAULT_SSL_OPTIONS = {
|
11
11
|
client_cert: nil,
|
@@ -37,10 +37,11 @@ module Kubeclient
|
|
37
37
|
DEFAULT_HTTP_MAX_REDIRECTS = 10
|
38
38
|
|
39
39
|
SEARCH_ARGUMENTS = {
|
40
|
-
'labelSelector'
|
41
|
-
'fieldSelector'
|
42
|
-
'
|
43
|
-
'
|
40
|
+
'labelSelector' => :label_selector,
|
41
|
+
'fieldSelector' => :field_selector,
|
42
|
+
'resourceVersion' => :resource_version,
|
43
|
+
'limit' => :limit,
|
44
|
+
'continue' => :continue
|
44
45
|
}.freeze
|
45
46
|
|
46
47
|
WATCH_ARGUMENTS = {
|
@@ -203,6 +204,7 @@ module Kubeclient
|
|
203
204
|
namespace.to_s.empty? ? '' : "namespaces/#{namespace}/"
|
204
205
|
end
|
205
206
|
|
207
|
+
# rubocop:disable Metrics/BlockLength
|
206
208
|
def define_entity_methods
|
207
209
|
@entities.values.each do |entity|
|
208
210
|
# get all entities of a type e.g. get_nodes, get_pods, etc.
|
@@ -211,12 +213,12 @@ module Kubeclient
|
|
211
213
|
end
|
212
214
|
|
213
215
|
# watch all entities of a type e.g. watch_nodes, watch_pods, etc.
|
214
|
-
define_singleton_method("watch_#{entity.method_names[1]}") do |options = {}|
|
216
|
+
define_singleton_method("watch_#{entity.method_names[1]}") do |options = {}, &block|
|
215
217
|
# This method used to take resource_version as a param, so
|
216
218
|
# this conversion is to keep backwards compatibility
|
217
219
|
options = { resource_version: options } unless options.is_a?(Hash)
|
218
220
|
|
219
|
-
watch_entities(entity.resource_name, options)
|
221
|
+
watch_entities(entity.resource_name, options, &block)
|
220
222
|
end
|
221
223
|
|
222
224
|
# get a single entity of a specific type by name
|
@@ -227,7 +229,7 @@ module Kubeclient
|
|
227
229
|
|
228
230
|
define_singleton_method("delete_#{entity.method_names[0]}") \
|
229
231
|
do |name, namespace = nil, opts = {}|
|
230
|
-
delete_entity(entity.resource_name, name, namespace, opts)
|
232
|
+
delete_entity(entity.resource_name, name, namespace, **opts)
|
231
233
|
end
|
232
234
|
|
233
235
|
define_singleton_method("create_#{entity.method_names[0]}") do |entity_config|
|
@@ -238,11 +240,23 @@ module Kubeclient
|
|
238
240
|
update_entity(entity.resource_name, entity_config)
|
239
241
|
end
|
240
242
|
|
241
|
-
define_singleton_method("patch_#{entity.method_names[0]}")
|
242
|
-
|
243
|
+
define_singleton_method("patch_#{entity.method_names[0]}") \
|
244
|
+
do |name, patch, namespace = nil|
|
245
|
+
patch_entity(entity.resource_name, name, patch, 'strategic-merge-patch', namespace)
|
246
|
+
end
|
247
|
+
|
248
|
+
define_singleton_method("json_patch_#{entity.method_names[0]}") \
|
249
|
+
do |name, patch, namespace = nil|
|
250
|
+
patch_entity(entity.resource_name, name, patch, 'json-patch', namespace)
|
251
|
+
end
|
252
|
+
|
253
|
+
define_singleton_method("merge_patch_#{entity.method_names[0]}") \
|
254
|
+
do |name, patch, namespace = nil|
|
255
|
+
patch_entity(entity.resource_name, name, patch, 'merge-patch', namespace)
|
243
256
|
end
|
244
257
|
end
|
245
258
|
end
|
259
|
+
# rubocop:enable Metrics/BlockLength
|
246
260
|
|
247
261
|
def self.underscore_entity(entity_name)
|
248
262
|
entity_name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
@@ -281,7 +295,9 @@ module Kubeclient
|
|
281
295
|
# :as (:raw|:ros) - defaults to :ros
|
282
296
|
# :raw - return the raw response body as a string
|
283
297
|
# :ros - return a collection of RecursiveOpenStruct objects
|
284
|
-
|
298
|
+
# Accepts an optional block, that will be called with each entity,
|
299
|
+
# otherwise returns a WatchStream
|
300
|
+
def watch_entities(resource_name, options = {}, &block)
|
285
301
|
ns = build_namespace_prefix(options[:namespace])
|
286
302
|
|
287
303
|
path = "watch/#{ns}#{resource_name}"
|
@@ -292,11 +308,13 @@ module Kubeclient
|
|
292
308
|
WATCH_ARGUMENTS.each { |k, v| params[k] = options[v] if options[v] }
|
293
309
|
uri.query = URI.encode_www_form(params) if params.any?
|
294
310
|
|
295
|
-
Kubeclient::Common::WatchStream.new(
|
311
|
+
watcher = Kubeclient::Common::WatchStream.new(
|
296
312
|
uri,
|
297
313
|
http_options(uri),
|
298
314
|
formatter: ->(value) { format_response(options[:as] || @as, value) }
|
299
315
|
)
|
316
|
+
|
317
|
+
return_or_yield_to_watcher(watcher, &block)
|
300
318
|
end
|
301
319
|
|
302
320
|
# Accepts the following options:
|
@@ -381,13 +399,13 @@ module Kubeclient
|
|
381
399
|
format_response(@as, response.body)
|
382
400
|
end
|
383
401
|
|
384
|
-
def patch_entity(resource_name, name, patch, namespace
|
402
|
+
def patch_entity(resource_name, name, patch, strategy, namespace)
|
385
403
|
ns_prefix = build_namespace_prefix(namespace)
|
386
404
|
response = handle_exception do
|
387
405
|
rest_client[ns_prefix + resource_name + "/#{name}"]
|
388
406
|
.patch(
|
389
407
|
patch.to_json,
|
390
|
-
{ 'Content-Type' =>
|
408
|
+
{ 'Content-Type' => "application/#{strategy}+json" }.merge(@headers)
|
391
409
|
)
|
392
410
|
end
|
393
411
|
format_response(@as, response.body)
|
@@ -409,13 +427,14 @@ module Kubeclient
|
|
409
427
|
|
410
428
|
def get_pod_log(pod_name, namespace,
|
411
429
|
container: nil, previous: false,
|
412
|
-
timestamps: false, since_time: nil, tail_lines: nil)
|
430
|
+
timestamps: false, since_time: nil, tail_lines: nil, limit_bytes: nil)
|
413
431
|
params = {}
|
414
432
|
params[:previous] = true if previous
|
415
433
|
params[:container] = container if container
|
416
434
|
params[:timestamps] = timestamps if timestamps
|
417
435
|
params[:sinceTime] = format_datetime(since_time) if since_time
|
418
436
|
params[:tailLines] = tail_lines if tail_lines
|
437
|
+
params[:limitBytes] = limit_bytes if limit_bytes
|
419
438
|
|
420
439
|
ns = build_namespace_prefix(namespace)
|
421
440
|
handle_exception do
|
@@ -424,7 +443,7 @@ module Kubeclient
|
|
424
443
|
end
|
425
444
|
end
|
426
445
|
|
427
|
-
def watch_pod_log(pod_name, namespace, container: nil)
|
446
|
+
def watch_pod_log(pod_name, namespace, container: nil, &block)
|
428
447
|
# Adding the "follow=true" query param tells the Kubernetes API to keep
|
429
448
|
# the connection open and stream updates to the log.
|
430
449
|
params = { follow: true }
|
@@ -436,7 +455,10 @@ module Kubeclient
|
|
436
455
|
uri.path += "/#{@api_version}/#{ns}pods/#{pod_name}/log"
|
437
456
|
uri.query = URI.encode_www_form(params)
|
438
457
|
|
439
|
-
Kubeclient::Common::WatchStream.new(
|
458
|
+
watcher = Kubeclient::Common::WatchStream.new(
|
459
|
+
uri, http_options(uri), formatter: ->(value) { value }
|
460
|
+
)
|
461
|
+
return_or_yield_to_watcher(watcher, &block)
|
440
462
|
end
|
441
463
|
|
442
464
|
def proxy_url(kind, name, port, namespace = '')
|
@@ -573,6 +595,16 @@ module Kubeclient
|
|
573
595
|
raise ArgumentError, msg unless File.readable?(@auth_options[:bearer_token_file])
|
574
596
|
end
|
575
597
|
|
598
|
+
def return_or_yield_to_watcher(watcher, &block)
|
599
|
+
return watcher unless block_given?
|
600
|
+
|
601
|
+
begin
|
602
|
+
watcher.each(&block)
|
603
|
+
ensure
|
604
|
+
watcher.finish
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
576
608
|
def http_options(uri)
|
577
609
|
options = {
|
578
610
|
basic_auth_user: @auth_options[:username],
|
data/lib/kubeclient/config.rb
CHANGED
@@ -150,9 +150,10 @@ module Kubeclient
|
|
150
150
|
if user.key?('token')
|
151
151
|
options[:bearer_token] = user['token']
|
152
152
|
elsif user.key?('exec')
|
153
|
-
exec_opts = user['exec']
|
154
|
-
exec_opts['command'] = ext_command_path(exec_opts['command']) if exec_opts['command']
|
153
|
+
exec_opts = expand_command_option(user['exec'], 'command')
|
155
154
|
options[:bearer_token] = Kubeclient::ExecCredentials.token(exec_opts)
|
155
|
+
elsif user.key?('auth-provider')
|
156
|
+
options[:bearer_token] = fetch_token_from_provider(user['auth-provider'])
|
156
157
|
else
|
157
158
|
%w[username password].each do |attr|
|
158
159
|
options[attr.to_sym] = user[attr] if user.key?(attr)
|
@@ -160,5 +161,22 @@ module Kubeclient
|
|
160
161
|
end
|
161
162
|
options
|
162
163
|
end
|
164
|
+
|
165
|
+
def fetch_token_from_provider(auth_provider)
|
166
|
+
case auth_provider['name']
|
167
|
+
when 'gcp'
|
168
|
+
config = expand_command_option(auth_provider['config'], 'cmd-path')
|
169
|
+
Kubeclient::GCPAuthProvider.token(config)
|
170
|
+
when 'oidc'
|
171
|
+
Kubeclient::OIDCAuthProvider.token(auth_provider['config'])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def expand_command_option(config, key)
|
176
|
+
config = config.dup
|
177
|
+
config[key] = ext_command_path(config[key]) if config[key]
|
178
|
+
|
179
|
+
config
|
180
|
+
end
|
163
181
|
end
|
164
182
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kubeclient/google_application_default_credentials'
|
4
|
+
require 'kubeclient/gcp_command_credentials'
|
5
|
+
|
6
|
+
module Kubeclient
|
7
|
+
# Handle different ways to get a bearer token for Google Cloud Platform.
|
8
|
+
class GCPAuthProvider
|
9
|
+
class << self
|
10
|
+
def token(config)
|
11
|
+
if config.key?('cmd-path')
|
12
|
+
Kubeclient::GCPCommandCredentials.token(config)
|
13
|
+
else
|
14
|
+
Kubeclient::GoogleApplicationDefaultCredentials.token
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kubeclient
|
4
|
+
# Generates a bearer token for Google Cloud Platform.
|
5
|
+
class GCPCommandCredentials
|
6
|
+
class << self
|
7
|
+
def token(config)
|
8
|
+
require 'open3'
|
9
|
+
require 'shellwords'
|
10
|
+
require 'json'
|
11
|
+
require 'jsonpath'
|
12
|
+
|
13
|
+
cmd = config['cmd-path']
|
14
|
+
args = config['cmd-args']
|
15
|
+
token_key = config['token-key']
|
16
|
+
|
17
|
+
out, err, st = Open3.capture3(cmd, *args.split(' '))
|
18
|
+
|
19
|
+
raise "exec command failed: #{err}" unless st.success?
|
20
|
+
|
21
|
+
extract_token(out, token_key)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def extract_token(output, token_key)
|
27
|
+
JsonPath.on(output, token_key.gsub(/^{|}$/, '')).first
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -3,10 +3,25 @@
|
|
3
3
|
module Kubeclient
|
4
4
|
# Get a bearer token from the Google's application default credentials.
|
5
5
|
class GoogleApplicationDefaultCredentials
|
6
|
+
class GoogleDependencyError < LoadError # rubocop:disable Lint/InheritException
|
7
|
+
end
|
8
|
+
|
6
9
|
class << self
|
7
10
|
def token
|
8
|
-
|
9
|
-
|
11
|
+
begin
|
12
|
+
require 'googleauth'
|
13
|
+
rescue LoadError => e
|
14
|
+
raise GoogleDependencyError,
|
15
|
+
'Error requiring googleauth gem. Kubeclient itself does not include the ' \
|
16
|
+
'googleauth gem. To support auth-provider gcp, you must include it in your ' \
|
17
|
+
"calling application. Failed with: #{e.message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
scopes = [
|
21
|
+
'https://www.googleapis.com/auth/cloud-platform',
|
22
|
+
'https://www.googleapis.com/auth/userinfo.email'
|
23
|
+
]
|
24
|
+
|
10
25
|
authorization = Google::Auth.get_application_default(scopes)
|
11
26
|
authorization.apply({})
|
12
27
|
authorization.access_token
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kubeclient
|
4
|
+
# Uses OIDC id-tokens and refreshes them if they are stale.
|
5
|
+
class OIDCAuthProvider
|
6
|
+
class OpenIDConnectDependencyError < LoadError # rubocop:disable Lint/InheritException
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def token(provider_config)
|
11
|
+
begin
|
12
|
+
require 'openid_connect'
|
13
|
+
rescue LoadError => e
|
14
|
+
raise OpenIDConnectDependencyError,
|
15
|
+
'Error requiring openid_connect gem. Kubeclient itself does not include the ' \
|
16
|
+
'openid_connect gem. To support auth-provider oidc, you must include it in your ' \
|
17
|
+
"calling application. Failed with: #{e.message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
issuer_url = provider_config['idp-issuer-url']
|
21
|
+
discovery = OpenIDConnect::Discovery::Provider::Config.discover! issuer_url
|
22
|
+
|
23
|
+
if provider_config.key? 'id-token'
|
24
|
+
return provider_config['id-token'] unless expired?(provider_config['id-token'], discovery)
|
25
|
+
end
|
26
|
+
|
27
|
+
client = OpenIDConnect::Client.new(
|
28
|
+
identifier: provider_config['client-id'],
|
29
|
+
secret: provider_config['client-secret'],
|
30
|
+
authorization_endpoint: discovery.authorization_endpoint,
|
31
|
+
token_endpoint: discovery.token_endpoint,
|
32
|
+
userinfo_endpoint: discovery.userinfo_endpoint
|
33
|
+
)
|
34
|
+
client.refresh_token = provider_config['refresh-token']
|
35
|
+
client.access_token!.id_token
|
36
|
+
end
|
37
|
+
|
38
|
+
def expired?(id_token, discovery)
|
39
|
+
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode(
|
40
|
+
id_token,
|
41
|
+
discovery.jwks
|
42
|
+
)
|
43
|
+
# If token expired or expiring within 60 seconds
|
44
|
+
Time.now.to_i + 60 > decoded_token.exp.to_i
|
45
|
+
rescue JSON::JWK::Set::KidNotFound
|
46
|
+
# Token cannot be verified: the kid it was signed with is not available for discovery
|
47
|
+
# Consider it expired and fetch a new one.
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|