New Upstream Release - ruby-json-jwt
Ready changes
Summary
Merged new upstream version: 1.16.3 (was: 1.15.3).
Diff
diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml
new file mode 100644
index 0000000..19509fb
--- /dev/null
+++ b/.github/workflows/spec.yml
@@ -0,0 +1,31 @@
+name: Spec
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ spec:
+ strategy:
+ matrix:
+ os: ['ubuntu-20.04', 'ubuntu-22.04']
+ ruby-version: ['3.1', '3.2']
+ include:
+ - os: 'ubuntu-20.04'
+ ruby-version: '3.0'
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby-version }}
+ bundler-cache: true
+ - name: Run Specs
+ run: bundle exec rake spec
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 55db3e4..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-before_install:
- - gem install bundler
- - git submodule update --init --recursive
-
-rvm:
- - 2.5.8
- - 2.6.6
- - 2.7.2
- - 3.0.2
-
-jdk:
- - openjdk11
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..91720ff
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,17 @@
+## [Unreleased]
+
+## [1.16.0] - 2022-10-08
+
+### Fixed
+
+- Remove padding oracle by @btoews in https://github.com/nov/json-jwt/pull/109
+
+## [1.16.0] - 2022-10-08
+
+### Added
+
+- start recording CHANGELOG
+
+### Changed
+
+* Switch from httpclient to faraday v2 https://github.com/nov/json-jwt/pull/110
\ No newline at end of file
diff --git a/README.md b/README.md
index 1ea16c4..fe6c579 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,6 @@
JSON Web Token and its family (JSON Web Signature, JSON Web Encryption and JSON Web Key) in Ruby
-[![Build Status](https://secure.travis-ci.org/nov/json-jwt.png)](http://travis-ci.org/nov/json-jwt)
-
## Installation
```
@@ -49,6 +47,17 @@ input = "jwt_header.jwt_claims.jwt_signature"
JSON::JWT.decode(input, public_key)
```
+If you need to get a JWK from `jwks_uri` of OpenID Connect IdP, you can use `JSON::JWK::Set::Fetcher` to fetch (& optionally cache) it.
+
+```ruby
+# JWK Set Fetching & Caching
+# NOTE: Optionally by setting cache instance, JWKs are cached by kid.
+JSON::JWK::Set::Fetcher.cache = Rails.cache
+
+JSON::JWK::Set::Fetcher.fetch(jwks_uri, kid: kid)
+# => returns JSON::JWK instance or raise JSON::JWK::Set::KidNotFound
+```
+
For more details, read [Documentation Wiki](https://github.com/nov/json-jwt/wiki).
## Note on Patches/Pull Requests
diff --git a/VERSION b/VERSION
index cd99d38..b8ae5a5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.14.0
\ No newline at end of file
+1.16.3
\ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
index 9347cad..97cd739 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+ruby-json-jwt (1.16.3-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Tue, 06 Jun 2023 20:41:49 -0000
+
ruby-json-jwt (1.14.0-2) unstable; urgency=medium
* No-change rebuild for unstable. (Closes: #1011682)
diff --git a/debian/patches/001-remove-simplecov.patch b/debian/patches/001-remove-simplecov.patch
index e6aebe3..858b4d7 100644
--- a/debian/patches/001-remove-simplecov.patch
+++ b/debian/patches/001-remove-simplecov.patch
@@ -7,10 +7,10 @@ Forwarded: not-needed
spec/spec_helper.rb | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
-diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
-index b27d079..145598b 100644
---- a/spec/spec_helper.rb
-+++ b/spec/spec_helper.rb
+Index: ruby-json-jwt.git/spec/spec_helper.rb
+===================================================================
+--- ruby-json-jwt.git.orig/spec/spec_helper.rb
++++ ruby-json-jwt.git/spec/spec_helper.rb
@@ -1,8 +1,8 @@
-require 'simplecov'
+#require 'simplecov'
diff --git a/json-jwt.gemspec b/json-jwt.gemspec
index a9c73f7..943d1f4 100644
--- a/json-jwt.gemspec
+++ b/json-jwt.gemspec
@@ -16,8 +16,11 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency 'activesupport', '>= 4.2'
gem.add_runtime_dependency 'bindata'
gem.add_runtime_dependency 'aes_key_wrap'
+ gem.add_runtime_dependency 'faraday', '~> 2.0'
+ gem.add_runtime_dependency 'faraday-follow_redirects'
gem.add_development_dependency 'rake'
gem.add_development_dependency 'simplecov'
+ gem.add_development_dependency 'webmock'
gem.add_development_dependency 'rspec'
gem.add_development_dependency 'rspec-its'
end
diff --git a/lib/json/jose.rb b/lib/json/jose.rb
index 53dfa93..eb94b10 100644
--- a/lib/json/jose.rb
+++ b/lib/json/jose.rb
@@ -26,9 +26,7 @@ module JSON
when JSON::JWK
key.to_key
when JSON::JWK::Set
- key.detect do |jwk|
- jwk[:kid] && jwk[:kid] == kid
- end&.to_key or raise JWK::Set::KidNotFound
+ key[kid]&.to_key or raise JWK::Set::KidNotFound
else
key
end
diff --git a/lib/json/jwe.rb b/lib/json/jwe.rb
index a46f2d1..eea1c9c 100644
--- a/lib/json/jwe.rb
+++ b/lib/json/jwe.rb
@@ -43,9 +43,12 @@ module JSON
raise UnexpectedAlgorithm.new('Unexpected alg header') unless algorithms.blank? || Array(algorithms).include?(alg)
raise UnexpectedAlgorithm.new('Unexpected enc header') unless encryption_methods.blank? || Array(encryption_methods).include?(enc)
self.private_key_or_secret = with_jwk_support private_key_or_secret
- cipher.decrypt
self.content_encryption_key = decrypt_content_encryption_key
self.mac_key, self.encryption_key = derive_encryption_and_mac_keys
+
+ verify_cbc_authentication_tag! if cbc?
+
+ cipher.decrypt
cipher.key = encryption_key
cipher.iv = iv # NOTE: 'iv' has to be set after 'key' for GCM
if gcm?
@@ -54,8 +57,15 @@ module JSON
cipher.auth_tag = authentication_tag
cipher.auth_data = auth_data
end
- self.plain_text = cipher.update(cipher_text) + cipher.final
- verify_cbc_authentication_tag! if cbc?
+
+ begin
+ self.plain_text = cipher.update(cipher_text) + cipher.final
+ rescue OpenSSL::OpenSSLError
+ # Ensure that the same error is raised for invalid PKCS7 padding
+ # as for invalid signatures. This prevents padding-oracle attacks.
+ raise DecryptionFailed
+ end
+
self
end
@@ -244,7 +254,7 @@ module JSON
sha_digest, mac_key, secured_input
)[0, sha_size / 2 / 8]
unless secure_compare(authentication_tag, expected_authentication_tag)
- raise DecryptionFailed.new('Invalid authentication tag')
+ raise DecryptionFailed
end
end
diff --git a/lib/json/jwk/set.rb b/lib/json/jwk/set.rb
index be9752f..9a66160 100644
--- a/lib/json/jwk/set.rb
+++ b/lib/json/jwk/set.rb
@@ -19,6 +19,12 @@ module JSON
'application/jwk-set+json'
end
+ def [](kid)
+ detect do |jwk|
+ jwk[:kid] && jwk[:kid] == kid
+ end
+ end
+
def as_json(options = {})
# NOTE: Array.new wrapper is requied to avoid CircularReferenceError
{keys: Array.new(self)}
diff --git a/lib/json/jwk/set/fetcher.rb b/lib/json/jwk/set/fetcher.rb
new file mode 100644
index 0000000..6d3d577
--- /dev/null
+++ b/lib/json/jwk/set/fetcher.rb
@@ -0,0 +1,86 @@
+module JSON
+ class JWK
+ class Set
+ module Fetcher
+ class Cache
+ def fetch(cache_key, options = {})
+ yield
+ end
+
+ def delete(cache_key, options = {}); end
+ end
+
+ def self.logger
+ @@logger
+ end
+ def self.logger=(logger)
+ @@logger = logger
+ end
+ self.logger = Logger.new(STDOUT)
+ self.logger.progname = 'JSON::JWK::Set::Fetcher'
+
+ def self.debugging?
+ @@debugging
+ end
+ def self.debugging=(boolean)
+ @@debugging = boolean
+ end
+ def self.debug!
+ self.debugging = true
+ end
+ def self.debug(&block)
+ original = self.debugging?
+ debug!
+ yield
+ ensure
+ self.debugging = original
+ end
+ self.debugging = false
+
+ def self.http_client
+ Faraday.new(headers: {user_agent: "JSON::JWK::Set::Fetcher #{VERSION}"}) do |faraday|
+ faraday.response :raise_error
+ faraday.response :follow_redirects
+ faraday.response :logger, JSON::JWK::Set::Fetcher.logger if debugging?
+ faraday.adapter Faraday.default_adapter
+ http_config.try(:call, faraday)
+ end
+ end
+ def self.http_config(&block)
+ @@http_config ||= block
+ end
+
+ def self.cache=(cache)
+ @@cache = cache
+ end
+ def self.cache
+ @@cache
+ end
+ self.cache = Cache.new
+
+ def self.fetch(jwks_uri, kid:, auto_detect: true, **options)
+ cache_key = [
+ 'json:jwk:set',
+ OpenSSL::Digest::MD5.hexdigest(jwks_uri),
+ kid
+ ].collect(&:to_s).join(':')
+
+ jwks = Set.new(
+ JSON.parse(
+ cache.fetch(cache_key, options) do
+ http_client.get(jwks_uri).body
+ end
+ )
+ )
+ cache.delete(cache_key, options) if jwks[kid].blank?
+
+ if auto_detect
+ jwks[kid] or raise KidNotFound
+ else
+ jwks
+ end
+ end
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/json/jwt.rb b/lib/json/jwt.rb
index 46f2732..9958751 100644
--- a/lib/json/jwt.rb
+++ b/lib/json/jwt.rb
@@ -1,11 +1,17 @@
require 'openssl'
require 'base64'
+require 'faraday'
+require 'faraday/follow_redirects'
require 'active_support'
require 'active_support/core_ext'
require 'json/jose'
module JSON
class JWT < ActiveSupport::HashWithIndifferentAccess
+ VERSION = ::File.read(
+ ::File.join(::File.dirname(__FILE__), '../../VERSION')
+ ).chomp
+
attr_accessor :blank_payload
attr_accessor :signature
@@ -132,3 +138,4 @@ require 'json/jwe'
require 'json/jwk'
require 'json/jwk/jwkizable'
require 'json/jwk/set'
+require 'json/jwk/set/fetcher'
\ No newline at end of file
diff --git a/spec/helpers/webmock_helper.rb b/spec/helpers/webmock_helper.rb
new file mode 100644
index 0000000..5a26b12
--- /dev/null
+++ b/spec/helpers/webmock_helper.rb
@@ -0,0 +1,34 @@
+require 'webmock/rspec'
+
+module WebMockHelper
+ def mock_json(method, endpoint, response_file, options = {})
+ stub_request(method, endpoint).to_return(
+ response_for(response_file, options)
+ )
+ result = yield
+ a_request(method, endpoint).should have_been_made.once
+ result
+ end
+
+ def request_to(endpoint, method = :get)
+ raise_error { |e|
+ e.should be_instance_of WebMock::NetConnectNotAllowedError
+ e.message.should include("Unregistered request: #{method.to_s.upcase}")
+ e.message.should include(endpoint)
+ }
+ end
+
+ private
+
+ def response_for(response_file, options = {})
+ response = {}
+ response[:body] = File.new(File.join(File.dirname(__FILE__), '../mock_response', "#{response_file}.#{options[:format] || :json}"))
+ if options[:status]
+ response[:status] = options[:status]
+ end
+ response
+ end
+end
+
+include WebMockHelper
+WebMock.disable_net_connect!
diff --git a/spec/json/jwe_spec.rb b/spec/json/jwe_spec.rb
index 18a3d28..598e937 100644
--- a/spec/json/jwe_spec.rb
+++ b/spec/json/jwe_spec.rb
@@ -91,22 +91,60 @@ describe JSON::JWE do
end
shared_examples_for :verify_cbc_authentication_tag do
- let(:jwe_string) do
+ let(:jwe_parts) do
_jwe_ = JSON::JWE.new plain_text
_jwe_.alg, _jwe_.enc = alg, enc
_jwe_.encrypt! key
- hdr, extra, iv, cipher_text, _ = _jwe_.to_s.split '.'
- [hdr, extra, iv, cipher_text, ''].join '.'
+ _jwe_.to_s.split '.'
end
- it do
- # fetching those variables outside of exception block to make sure
- # we intercept exception in decrypt! and not in other place
- j = jwe
- k = key
- expect do
- j.decrypt! k
- end.to raise_error JSON::JWE::DecryptionFailed
+ let(:hdr) { jwe_parts[0] }
+ let(:extra) { jwe_parts[1] }
+ let(:iv) { jwe_parts[2] }
+ let(:cipher_text) { jwe_parts[3] }
+ let(:signature) { jwe_parts[4] }
+
+ let(:jwe_string) { [hdr, extra, iv, cipher_text, signature].join '.' }
+
+ shared_examples_for :signature_verification_failure do
+ it do
+ # fetching those variables outside of exception block to make sure
+ # we intercept exception in decrypt! and not in other place
+ j = jwe
+ k = key
+ expect do
+ j.decrypt! k
+ end.to raise_error JSON::JWE::DecryptionFailed
+ end
+ end
+
+ describe "with missing signature" do
+ let(:signature) { "" }
+ it_behaves_like :signature_verification_failure
+ end
+
+ describe "with good pkcs7 padding and bad signature" do
+ let(:iv) do
+ good_padding_byte = 16 - (plain_text.bytesize % 16)
+ bad_padding_byte = 1
+ iv_bytes = Base64.urlsafe_decode64(super()).bytes
+ iv_bytes[-1] = iv_bytes[-1] ^ good_padding_byte ^ bad_padding_byte
+ Base64.urlsafe_encode64(iv_bytes.pack("C*"), padding: false)
+ end
+
+ it_behaves_like :signature_verification_failure
+ end
+
+ describe "with bad pkcs7 padding and bad signature" do
+ let(:iv) do
+ good_padding_byte = 16 - (plain_text.bytesize % 16)
+ bad_padding_byte = good_padding_byte - 1
+ iv_bytes = Base64.urlsafe_decode64(super()).bytes
+ iv_bytes[-1] = iv_bytes[-1] ^ good_padding_byte ^ bad_padding_byte
+ Base64.urlsafe_encode64(iv_bytes.pack("C*"), padding: false)
+ end
+
+ it_behaves_like :signature_verification_failure
end
end
diff --git a/spec/json/jwk/set/fetcher_spec.rb b/spec/json/jwk/set/fetcher_spec.rb
new file mode 100644
index 0000000..5d14783
--- /dev/null
+++ b/spec/json/jwk/set/fetcher_spec.rb
@@ -0,0 +1,227 @@
+require 'spec_helper'
+
+describe JSON::JWK::Set::Fetcher do
+ describe JSON::JWK::Set::Fetcher::Cache do
+ let(:something) { SecureRandom.hex(32) }
+
+ it 'just execute givne block' do
+ expect(
+ subject.fetch('cache_key') do
+ something
+ end
+ ).to eq something
+ end
+ end
+
+ describe 'debugging feature' do
+ after { JSON::JWK::Set::Fetcher.debugging = false }
+
+ its(:logger) { should be_a Logger }
+ its(:debugging?) { should == false }
+
+ describe '.debug!' do
+ before { JSON::JWK::Set::Fetcher.debug! }
+ its(:debugging?) { should == true }
+ end
+
+ describe '.debug' do
+ it 'should enable debugging within given block' do
+ JSON::JWK::Set::Fetcher.debug do
+ JSON::JWK::Set::Fetcher.debugging?.should == true
+ end
+ JSON::JWK::Set::Fetcher.debugging?.should == false
+ end
+
+ it 'should not force disable debugging' do
+ JSON::JWK::Set::Fetcher.debug!
+ JSON::JWK::Set::Fetcher.debug do
+ JSON::JWK::Set::Fetcher.debugging?.should == true
+ end
+ JSON::JWK::Set::Fetcher.debugging?.should == true
+ end
+ end
+ end
+
+ describe '.http_client' do
+ context 'with http_config' do
+ before do
+ JSON::JWK::Set::Fetcher.http_config do |config|
+ config.ssl.verify = false
+ end
+ end
+ it 'should configure OpenIDConnect, SWD and Rack::OAuth2\'s http_client' do
+ JSON::JWK::Set::Fetcher.http_client.ssl.verify = false
+ end
+ end
+ end
+
+ describe 'fetching feature' do
+ class CustomCache
+ JWKS_URI = 'https://idp.example.com/jwks'
+
+ def fetch(cache_key, options = {})
+ base_key = "json:jwk:set:#{OpenSSL::Digest::MD5.hexdigest JWKS_URI}"
+ case cache_key
+ when "#{base_key}:known"
+ File.read(File.join(File.dirname(__FILE__), '../../../mock_response/jwks.json'))
+ else
+ yield
+ end
+ end
+
+ def delete(cache_key)
+ # ignore
+ end
+ end
+
+ let(:jwks_uri) { CustomCache::JWKS_URI }
+
+ describe '.cache' do
+ subject { JSON::JWK::Set::Fetcher.cache }
+
+ context 'as default' do
+ it { should be_instance_of JSON::JWK::Set::Fetcher::Cache }
+ end
+
+ context 'when specified' do
+ around do |example|
+ JSON::JWK::Set::Fetcher.cache = CustomCache.new
+ example.run
+ JSON::JWK::Set::Fetcher.cache = JSON::JWK::Set::Fetcher::Cache.new
+ end
+ it { should be_instance_of CustomCache }
+ end
+ end
+
+ describe '.fetch' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, kid: kid }
+
+ around do |example|
+ JSON::JWK::Set::Fetcher.cache = CustomCache.new
+ example.run
+ JSON::JWK::Set::Fetcher.cache = JSON::JWK::Set::Fetcher::Cache.new
+ end
+
+ context 'when not cached' do
+ let(:kid) { 'not_cached' }
+
+ it "should request to jwks_uri" do
+ expect do
+ subject
+ end.to request_to jwks_uri
+ end
+
+ context 'when unknown' do
+ let(:kid) { 'unknown' }
+ let(:cache_key) do
+ [
+ 'json:jwk:set',
+ OpenSSL::Digest::MD5.hexdigest(jwks_uri),
+ kid
+ ].collect(&:to_s).join(':')
+ end
+
+ it do
+ expect(JSON::JWK::Set::Fetcher.cache).to receive(:delete).with(cache_key, {})
+ expect do
+ mock_json :get, jwks_uri, 'jwks' do
+ subject
+ end
+ end.to raise_error JSON::JWK::Set::KidNotFound
+ end
+ end
+ end
+
+ context 'when cached' do
+ context 'when known' do
+ let(:kid) { 'known' }
+
+ it "should not request to jwks_uri" do
+ expect do
+ subject
+ end.not_to request_to jwks_uri
+ end
+
+ it do
+ should be_instance_of JSON::JWK
+ end
+
+ context 'when auto_detect disabled' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, kid: kid, auto_detect: false }
+
+ it do
+ should be_instance_of JSON::JWK::Set
+ end
+ end
+ end
+ end
+
+ describe 'cache options' do
+ let(:kid) { 'known' }
+
+ shared_context :receive_options_as_hash do
+ it do
+ expect_any_instance_of(CustomCache).to receive(:fetch).with(
+ "json:jwk:set:#{OpenSSL::Digest::MD5.hexdigest CustomCache::JWKS_URI}:#{kid}",
+ options
+ ).and_return(
+ File.read(File.join(File.dirname(__FILE__), '../../../mock_response/jwks.json'))
+ )
+ subject
+ end
+ end
+ shared_context :receive_options_as_blank_hash do
+ let(:options) { {} }
+ it_behaves_like :receive_options_as_hash
+ end
+
+ context 'when cache options not given' do
+ context 'with auto_detect' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, kid: kid }
+ it_behaves_like :receive_options_as_blank_hash
+ end
+ end
+
+ context 'when cache options given' do
+ let(:options) do
+ {
+ force: true,
+ expires_in: 10.minutes
+ }
+ end
+
+ context 'with auto_detect' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, kid: kid, auto_detect: true, **options }
+ it_behaves_like :receive_options_as_hash
+ end
+
+ context 'without auto_detect' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, kid: kid, **options }
+ it_behaves_like :receive_options_as_hash
+ end
+
+ context 'when kid & auto_detect are included in the given options' do
+ context 'as hash' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, **options.merge(kid: kid, auto_detect: true) }
+ it_behaves_like :receive_options_as_hash
+ end
+
+ context 'as keyward args' do
+ subject { JSON::JWK::Set::Fetcher.fetch jwks_uri, options.merge(kid: kid, auto_detect: true) }
+
+ if Gem.ruby_version >= Gem::Version.create(3.0)
+ it do
+ expect do
+ subject
+ end.to raise_error ArgumentError
+ end
+ else
+ it_behaves_like :receive_options_as_hash
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/json/jwt_spec.rb b/spec/json/jwt_spec.rb
index 75aeeb3..6305133 100644
--- a/spec/json/jwt_spec.rb
+++ b/spec/json/jwt_spec.rb
@@ -19,6 +19,10 @@ describe JSON::JWT do
'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.'
end
+ its(:version) do
+ JSON::JWT::VERSION.should_not be_blank
+ end
+
context 'when not signed nor encrypted' do
it do
jwt.to_s.should == no_signed
diff --git a/spec/mock_response/jwks.json b/spec/mock_response/jwks.json
new file mode 100644
index 0000000..f0d185f
--- /dev/null
+++ b/spec/mock_response/jwks.json
@@ -0,0 +1,19 @@
+{
+ "keys": [{
+ "kty": "RSA",
+ "kid": "known",
+ "use": "sig",
+ "alg": "RS256",
+ "n": "lxrwmuYSAsTfn-lUu4goZSXBD9ackM9OJuwUVQHmbZo6GW4Fu_auUdN5zI7Y1dEDfgt7m7QXWbHuMD01HLnD4eRtY-RNwCWdjNfEaY_esUPY3OVMrNDI15Ns13xspWS3q-13kdGv9jHI28P87RvMpjz_JCpQ5IM44oSyRnYtVJO-320SB8E2Bw92pmrenbp67KRUzTEVfGU4-obP5RZ09OxvCr1io4KJvEOjDJuuoClF66AT72WymtoMdwzUmhINjR0XSqK6H0MdWsjw7ysyd_JhmqX5CAaT9Pgi0J8lU_pcl215oANqjy7Ob-VMhug9eGyxAWVfu_1u6QJKePlE-w",
+ "e": "AQAB"
+ },
+ {
+ "kty": "RSA",
+ "kid": "fh6Bs8C",
+ "use": "sig",
+ "alg": "RS256",
+ "n": "u704gotMSZc6CSSVNCZ1d0S9dZKwO2BVzfdTKYz8wSNm7R_KIufOQf3ru7Pph1FjW6gQ8zgvhnv4IebkGWsZJlodduTC7c0sRb5PZpEyM6PtO8FPHowaracJJsK1f6_rSLstLdWbSDXeSq7vBvDu3Q31RaoV_0YlEzQwPsbCvD45oVy5Vo5oBePUm4cqi6T3cZ-10gr9QJCVwvx7KiQsttp0kUkHM94PlxbG_HAWlEZjvAlxfEDc-_xZQwC6fVjfazs3j1b2DZWsGmBRdx1snO75nM7hpyRRQB4jVejW9TuZDtPtsNadXTr9I5NjxPdIYMORj9XKEh44Z73yfv0gtw",
+ "e": "AQAB"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index fb56f7c..01bd5f6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -16,3 +16,4 @@ end
require 'helpers/sign_key_fixture_helper'
require 'helpers/nimbus_spec_helper'
+require 'helpers/webmock_helper'
More details
Historical runs
- missing-ruby-gem: missing ruby gem: faraday-follow_redirects (>= 0)
- unsatisfied-apt-dependencies: Unsatisfied APT dependencies: ruby-faraday:amd64 (>= 2.0)
- unsatisfied-apt-dependencies: Unsatisfied APT dependencies: ruby-faraday:amd64 (>= 2.0)
- success: Merged new upstream version 1.15.3
- success: Merged new upstream version 1.15.3
- success: Merged new upstream version 1.15.3