Skip to content

Commit

Permalink
Bing back compatibility with old apis
Browse files Browse the repository at this point in the history
  • Loading branch information
anakinj committed Oct 2, 2024
1 parent 685a245 commit 7b7107e
Show file tree
Hide file tree
Showing 24 changed files with 611 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ coverage/
*gemfile.lock
.byebug_history
*.gem
doc/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

- Updated README to correctly document `OpenSSL::HMAC` documentation [#617](https://github.com/jwt/ruby-jwt/pull/617) ([@aedryan](https://github.com/aedryan))
- Verify JWT header format [#622](https://github.com/jwt/ruby-jwt/pull/622) ([@304](https://github.com/304))
- Bring back `::JWT::ClaimsValidator`, `::JWT::Verify` and a few other removed interfaces for preserved backwards compatibility [#624](https://github.com/jwt/ruby-jwt/pull/624) ([@anakinj](https://github.com/anakinj))
- Your contribution here

## [v2.9.1](https://github.com/jwt/ruby-jwt/tree/v2.9.1) (2024-09-23)
Expand Down
3 changes: 3 additions & 0 deletions lib/jwt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
require 'jwt/jwk'
require 'jwt/claims'

require 'jwt/claims_validator'
require 'jwt/verify'

# JSON Web Token implementation
#
# Should be up to date with the latest spec:
Expand Down
5 changes: 4 additions & 1 deletion lib/jwt/claims.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ module Claims
class << self
def verify!(payload, options)
VERIFIERS.each do |key, verifier_builder|
next unless options[key]
next unless options[key] || options[key.to_s]

verifier_builder&.call(options)&.verify!(context: VerificationContext.new(payload: payload))
end
nil
end
end

private_constant(:VERIFIERS)
end
end
14 changes: 14 additions & 0 deletions lib/jwt/claims_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module JWT
class ClaimsValidator
def initialize(payload)
Deprecations.warning('The ::JWT::ClaimsValidator class is deprecated and will be removed in the next major version of ruby-jwt')
@payload = payload
end

def validate!
Claims::Numeric.verify!(payload: @payload)
end
end
end
8 changes: 7 additions & 1 deletion lib/jwt/jwa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
raise if defined?(RbNaCl)
end

require_relative 'jwa/compat'
require_relative 'jwa/signing_algorithm'
require_relative 'jwa/ecdsa'
require_relative 'jwa/hmac'
Expand All @@ -34,12 +35,17 @@ def resolve(algorithm)
return find(algorithm) if algorithm.is_a?(String) || algorithm.is_a?(Symbol)

unless algorithm.is_a?(SigningAlgorithm)
Deprecations.warning('Custom algorithms are required to include JWT::JWA::SigningAlgorithm')
Deprecations.warning('Custom algorithms are required to include JWT::JWA::SigningAlgorithm. Custom algorithms that do not include this module may stop working in the next major version of ruby-jwt.')
return Wrapper.new(algorithm)
end

algorithm
end

def create(algorithm)
Deprecations.warning('The ::JWT::JWA.create method is deprecated and will be removed in the next major version of ruby-jwt.')
resolve(algorithm)
end
end
end
end
29 changes: 29 additions & 0 deletions lib/jwt/jwa/compat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module JWT
module JWA
module Compat
module ClassMethods
def from_algorithm(algorithm)
new(algorithm)
end

def sign(algorithm, msg, key)
Deprecations.warning('Support for calling sign with positional arguments will be removed in future ruby-jwt versions')

from_algorithm(algorithm).sign(data: msg, signing_key: key)
end

def verify(algorithm, key, signing_input, signature)
Deprecations.warning('Support for calling verify with positional arguments will be removed in future ruby-jwt versions')

from_algorithm(algorithm).verify(data: signing_input, signature: signature, verification_key: key)
end
end

def self.included(klass)
klass.extend(ClassMethods)
end
end
end
end
4 changes: 4 additions & 0 deletions lib/jwt/jwa/ecdsa.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def verify(data:, signature:, verification_key:)
register_algorithm(new(v[:algorithm], v[:digest]))
end

def self.from_algorithm(algorithm)
new(algorithm, algorithm.downcase.gsub('es', 'sha'))
end

def self.curve_by_name(name)
NAMED_CURVES.fetch(name) do
raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
Expand Down
4 changes: 4 additions & 0 deletions lib/jwt/jwa/hmac.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module JWA
class Hmac
include JWT::JWA::SigningAlgorithm

def self.from_algorithm(algorithm)
new(algorithm, OpenSSL::Digest.new(algorithm.downcase.gsub('hs', 'sha')))
end

def initialize(alg, digest)
@alg = alg
@digest = digest
Expand Down
4 changes: 4 additions & 0 deletions lib/jwt/jwa/hmac_rbnacl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module JWA
class HmacRbNaCl
include JWT::JWA::SigningAlgorithm

def self.from_algorithm(algorithm)
new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
end

def initialize(alg, hmac)
@alg = alg
@hmac = hmac
Expand Down
4 changes: 4 additions & 0 deletions lib/jwt/jwa/hmac_rbnacl_fixed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ module JWA
class HmacRbNaClFixed
include JWT::JWA::SigningAlgorithm

def self.from_algorithm(algorithm)
new(algorithm, ::RbNaCl::HMAC.const_get(algorithm.upcase.gsub('HS', 'SHA')))
end

def initialize(alg, hmac)
@alg = alg
@hmac = hmac
Expand Down
1 change: 1 addition & 0 deletions lib/jwt/jwa/signing_algorithm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def register_algorithm(algo)

def self.included(klass)
klass.extend(ClassMethods)
klass.include(JWT::JWA::Compat)
end

attr_reader :alg
Expand Down
37 changes: 37 additions & 0 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require 'jwt/error'

module JWT
# This class was used internally to verify JWT claims. Planned to be removed in ruby-jwt 3.0
# @private
class Verify # :nodoc:
DEFAULTS = { leeway: 0 }.freeze
METHODS = %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub verify_required_claims].freeze

class << self
METHODS.each do |method_name|
define_method(method_name) do |payload, options|
new(payload, options).send(method_name)
end
end

def verify_claims(payload, options)
Deprecations.warning('The ::JWT::Verify.verify_claims method is deprecated and will be removed in the next major version of ruby-jwt')
::JWT::Claims.verify!(payload, options)
end
end

def initialize(payload, options)
Deprecations.warning('The ::JWT::Verify class is deprecated and will be removed in the next major version of ruby-jwt')
@payload = payload
@options = DEFAULTS.merge(options)
end

METHODS.each do |method_name|
define_method(method_name) do
::JWT::Claims.verify!(@payload, @options.merge(method_name => true))
end
end
end
end
79 changes: 79 additions & 0 deletions spec/jwt/claims_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

RSpec.describe JWT::ClaimsValidator do
let(:validator) { described_class.new(claims) }

describe '#validate!' do
subject { validator.validate! }

shared_examples_for 'a NumericDate claim' do |claim|
context "when #{claim} payload is an integer" do
let(:claims) { { claim => 12_345 } }

it 'does not raise error' do
expect { subject }.not_to raise_error
end

context 'and key is a string' do
let(:claims) { { claim.to_s => 43.32 } }

it 'does not raise error' do
expect { subject }.not_to raise_error
end
end
end

context "when #{claim} payload is a float" do
let(:claims) { { claim => 43.32 } }

it 'does not raise error' do
expect { subject }.not_to raise_error
end
end

context "when #{claim} payload is a string" do
let(:claims) { { claim => '1' } }

it 'raises error' do
expect { subject }.to raise_error JWT::InvalidPayload
end

context 'and key is a string' do
let(:claims) { { claim.to_s => '1' } }

it 'raises error' do
expect { subject }.to raise_error JWT::InvalidPayload
end
end
end

context "when #{claim} payload is a Time object" do
let(:claims) { { claim => Time.now } }

it 'raises error' do
expect { subject }.to raise_error JWT::InvalidPayload
end
end

context "when #{claim} payload is a string" do
let(:claims) { { claim => '1' } }

it 'raises error' do
expect { subject }.to raise_error JWT::InvalidPayload
end
end
end

context 'exp claim' do
it_should_behave_like 'a NumericDate claim', :exp
end

context 'iat claim' do
it_should_behave_like 'a NumericDate claim', :iat
end

context 'nbf claim' do
it_should_behave_like 'a NumericDate claim', :nbf
end
end
end
8 changes: 8 additions & 0 deletions spec/jwt/jwa/ecdsa_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,12 @@
end
end
end

context 'backwards compatibility' do
it 'signs and verifies' do
key = OpenSSL::PKey::EC.generate('prime256v1')
signature = described_class.sign('ES256', 'data', key)
expect(described_class.verify('ES256', key, 'data', signature)).to be(true)
end
end
end
16 changes: 16 additions & 0 deletions spec/jwt/jwa/eddsa_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

RSpec.describe 'JWT::JWA::Eddsa' do
let(:key) { RbNaCl::Signatures::Ed25519::SigningKey.generate }

before do
skip('Requires the rbnacl gem') unless JWT.rbnacl?
end

context 'backwards compatibility' do
it 'signs and verifies' do
signature = JWT::JWA::Eddsa.sign('RS256', 'data', key)
expect(JWT::JWA::Eddsa.verify('RS256', key.verify_key, 'data', signature)).to be(true)
end
end
end
7 changes: 7 additions & 0 deletions spec/jwt/jwa/hmac_rbnacl_fixed_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,11 @@
it { is_expected.to be(false) }
end
end

context 'backwards compatibility' do
it 'signs and verifies' do
signature = JWT::JWA::HmacRbNaClFixed.sign('HS512256', 'data', 'key')
expect(JWT::JWA::HmacRbNaClFixed.verify('HS512256', 'key', 'data', signature)).to be(true)
end
end
end
13 changes: 13 additions & 0 deletions spec/jwt/jwa/hmac_rbnacl_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

RSpec.describe 'JWT::JWA::HmacRbNaCl' do
before do
skip('Requires the rbnacl gem') unless JWT.rbnacl_6_or_greater?
end
context 'backwards compatibility' do
it 'signs and verifies' do
signature = JWT::JWA::HmacRbNaCl.sign('HS512256', 'data', 'key')
expect(JWT::JWA::HmacRbNaCl.verify('HS512256', 'key', 'data', signature)).to be(true)
end
end
end
8 changes: 8 additions & 0 deletions spec/jwt/jwa/hmac_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,12 @@
it { is_expected.to be(false) }
end
end

context 'backwards compatibility' do
it 'signs and verifies' do
signature = described_class.sign('HS256', 'data', 'key')
expect(signature).to be_a(String)
expect(described_class.verify('HS256', 'key', 'data', signature)).to be(true)
end
end
end
7 changes: 7 additions & 0 deletions spec/jwt/jwa/ps_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,11 @@
end
end
end

context 'backwards compatibility' do
it 'signs and verifies' do
signature = described_class.sign('PS256', 'data', rsa_key)
expect(described_class.verify('PS256', rsa_key, 'data', signature)).to be(true)
end
end
end
7 changes: 7 additions & 0 deletions spec/jwt/jwa/rsa_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,11 @@
end
end
end

context 'backwards compatibility' do
it 'signs and verifies' do
signature = described_class.sign('RS256', 'data', rsa_key)
expect(described_class.verify('RS256', rsa_key, 'data', signature)).to be(true)
end
end
end
16 changes: 16 additions & 0 deletions spec/jwt/jwa_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

RSpec.describe JWT::JWA do
describe '.create' do
describe 'Backwards compatibility' do
describe 'create, sign and verify' do
it 'finds an algorithm with old api' do
alg = described_class.create('HS256')
signature = alg.sign(data: 'data', signing_key: 'key')
expect(signature).to be_a(String)
expect(alg.verify(data: 'data', signature: signature, verification_key: 'key')).to be(true)
end
end
end
end
end
2 changes: 1 addition & 1 deletion spec/jwt/jwt_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ def verify(*)
end

it 'emits a deprecation warning' do
expect { token }.to output("[DEPRECATION WARNING] Custom algorithms are required to include JWT::JWA::SigningAlgorithm\n").to_stderr
expect { token }.to output(/.*Custom algorithms are required to include JWT::JWA::SigningAlgorithm.*/).to_stderr
expect(JWT.decode(token, 'secret', true, algorithm: custom_algorithm.new)).to eq([payload, { 'alg' => 'custom', 'foo' => 'bar' }])
end
end
Expand Down
Loading

0 comments on commit 7b7107e

Please sign in to comment.