Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring back removed APIs #624

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
3 changes: 2 additions & 1 deletion lib/jwt/claims.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ 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
end
Expand Down
16 changes: 16 additions & 0 deletions lib/jwt/claims_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require_relative 'error'

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
35 changes: 35 additions & 0 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require_relative 'error'

module JWT
class Verify
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
Loading