diff --git a/lib/jwt.rb b/lib/jwt.rb index f8a93a88..cd09cf9c 100644 --- a/lib/jwt.rb +++ b/lib/jwt.rb @@ -13,9 +13,6 @@ require 'jwt/encoded_token' require 'jwt/token' -require 'jwt/claims_validator' -require 'jwt/verify' - # JSON Web Token implementation # # Should be up to date with the latest spec: diff --git a/lib/jwt/claims.rb b/lib/jwt/claims.rb index 22492a7d..45ed547b 100644 --- a/lib/jwt/claims.rb +++ b/lib/jwt/claims.rb @@ -32,12 +32,6 @@ module Claims Error = Struct.new(:message, keyword_init: true) class << self - # @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt. - def verify!(payload, options) - Deprecations.warning('The ::JWT::Claims.verify! method is deprecated will be removed in the next major version of ruby-jwt') - DecodeVerifier.verify!(payload, options) - end - # Checks if the claims in the JWT payload are valid. # @example # diff --git a/lib/jwt/claims/numeric.rb b/lib/jwt/claims/numeric.rb index 4a6f8bed..7589dc6b 100644 --- a/lib/jwt/claims/numeric.rb +++ b/lib/jwt/claims/numeric.rb @@ -5,18 +5,6 @@ module Claims # The Numeric class is responsible for validating numeric claims in a JWT token. # The numeric claims are: exp, iat and nbf class Numeric - # The Compat class provides backward compatibility for numeric claim validation. - # @api private - class Compat - def initialize(payload) - @payload = payload - end - - def verify! - JWT::Claims.verify_payload!(@payload, :numeric) - end - end - # List of numeric claims that can be validated. NUMERIC_CLAIMS = %i[ exp @@ -26,14 +14,6 @@ def verify! private_constant(:NUMERIC_CLAIMS) - # @api private - def self.new(*args) - return super if args.empty? - - Deprecations.warning('Calling ::JWT::Claims::Numeric.new with the payload will be removed in the next major version of ruby-jwt') - Compat.new(*args) - end - # Verifies the numeric claims in the JWT context. # # @param context [Object] the context containing the JWT payload. @@ -43,18 +23,6 @@ def verify!(context:) validate_numeric_claims(context.payload) end - # Verifies the numeric claims in the JWT payload. - # - # @param payload [Hash] the JWT payload containing the claims. - # @param _args [Hash] additional arguments (not used). - # @raise [JWT::InvalidClaimError] if any numeric claim is invalid. - # @return [nil] - # @deprecated The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt - def self.verify!(payload:, **_args) - Deprecations.warning('The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt.') - JWT::Claims.verify_payload!(payload, :numeric) - end - private def validate_numeric_claims(payload) diff --git a/lib/jwt/claims_validator.rb b/lib/jwt/claims_validator.rb deleted file mode 100644 index 24bd41ae..00000000 --- a/lib/jwt/claims_validator.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module JWT - # @deprecated Use `Claims.verify_payload!` directly instead. - class ClaimsValidator - # @deprecated Use `Claims.verify_payload!` directly instead. - 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 - - # @deprecated Use `Claims.verify_payload!` directly instead. - def validate! - Claims.verify_payload!(@payload, :numeric) - true - end - end -end diff --git a/lib/jwt/jwa.rb b/lib/jwt/jwa.rb index d2d25bc2..f1b23a4c 100644 --- a/lib/jwt/jwa.rb +++ b/lib/jwt/jwa.rb @@ -41,12 +41,6 @@ def resolve_and_sort(algorithms:, preferred_algorithm:) algs = Array(algorithms).map { |alg| JWA.resolve(alg) } algs.partition { |alg| alg.valid_alg?(preferred_algorithm) }.flatten end - - # @deprecated The `::JWT::JWA.create` method is deprecated and will be removed in the next major version of ruby-jwt. - 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 diff --git a/lib/jwt/verify.rb b/lib/jwt/verify.rb deleted file mode 100644 index eebe0fbf..00000000 --- a/lib/jwt/verify.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require_relative 'error' - -module JWT - # @deprecated This class is deprecated and will be removed in the next major version of ruby-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 - - private_constant(:DEFAULTS, :METHODS) - class << self - METHODS.each do |method_name| - define_method(method_name) do |payload, options| - new(payload, options).send(method_name) - end - end - - # @deprecated This method is deprecated and will be removed in the next major version of ruby-jwt. - 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) - true - end - end - - # @deprecated This class is deprecated and will be removed in the next major version of ruby-jwt. - 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 diff --git a/spec/jwt/claims/numeric_spec.rb b/spec/jwt/claims/numeric_spec.rb index d6a9e53b..e2f25467 100644 --- a/spec/jwt/claims/numeric_spec.rb +++ b/spec/jwt/claims/numeric_spec.rb @@ -91,23 +91,4 @@ it_should_behave_like 'a NumericDate claim', :nbf end end - - context 'Legacy use' do - let(:validator) { described_class.new(claims) } - describe '#verify!' do - subject { validator.verify! } - - 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 end diff --git a/spec/jwt/claims_validator_spec.rb b/spec/jwt/claims_validator_spec.rb deleted file mode 100644 index 6e05f068..00000000 --- a/spec/jwt/claims_validator_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -# 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 { is_expected.to be_truthy } - 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 diff --git a/spec/jwt/jwa_spec.rb b/spec/jwt/jwa_spec.rb index d7106cab..7eb59a75 100644 --- a/spec/jwt/jwa_spec.rb +++ b/spec/jwt/jwa_spec.rb @@ -1,18 +1,6 @@ # 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 describe '.resolve_and_sort' do let(:subject) { described_class.resolve_and_sort(algorithms: algorithms, preferred_algorithm: preferred_algorithm).map(&:alg) } diff --git a/spec/jwt/verify_spec.rb b/spec/jwt/verify_spec.rb deleted file mode 100644 index faa31636..00000000 --- a/spec/jwt/verify_spec.rb +++ /dev/null @@ -1,336 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe JWT::Verify do - let(:base_payload) { { 'user_id' => 'some@user.tld' } } - let(:string_payload) { 'beautyexperts_nbf_iat' } - let(:options) { { leeway: 0 } } - - context '.verify_aud(payload, options)' do - let(:scalar_aud) { 'ruby-jwt-aud' } - let(:array_aud) { %w[ruby-jwt-aud test-aud ruby-ruby-ruby] } - let(:scalar_payload) { base_payload.merge('aud' => scalar_aud) } - let(:array_payload) { base_payload.merge('aud' => array_aud) } - - it 'must raise JWT::InvalidAudError when the singular audience does not match' do - expect do - described_class.verify_aud(scalar_payload, options.merge(aud: 'no-match')) - end.to raise_error JWT::InvalidAudError - end - - it 'must raise JWT::InvalidAudError when the payload has an array and none match the supplied value' do - expect do - described_class.verify_aud(array_payload, options.merge(aud: 'no-match')) - end.to raise_error JWT::InvalidAudError - end - - it 'must allow a matching singular audience to pass' do - described_class.verify_aud(scalar_payload, options.merge(aud: scalar_aud)) - end - - it 'must allow an array with any value matching the one in the options' do - described_class.verify_aud(array_payload, options.merge(aud: array_aud.first)) - end - - it 'must allow an array with any value matching any value in the options array' do - described_class.verify_aud(array_payload, options.merge(aud: array_aud)) - end - - it 'must allow a singular audience payload matching any value in the options array' do - described_class.verify_aud(scalar_payload, options.merge(aud: array_aud)) - end - end - - context '.verify_expiration(payload, options)' do - let(:payload) { base_payload.merge('exp' => (Time.now.to_i - 5)) } - - it 'must raise JWT::ExpiredSignature when the token has expired' do - expect do - described_class.verify_expiration(payload, options) - end.to raise_error JWT::ExpiredSignature - end - - it 'must allow some leeway in the expiration when global leeway is configured' do - described_class.verify_expiration(payload, options.merge(leeway: 10)) - end - - it 'must allow some leeway in the expiration when exp_leeway is configured' do - described_class.verify_expiration(payload, options.merge(exp_leeway: 10)) - end - - it 'must be expired if the exp claim equals the current time' do - payload['exp'] = Time.now.to_i - - expect do - described_class.verify_expiration(payload, options) - end.to raise_error JWT::ExpiredSignature - end - - it 'must not consider string containing exp as expired' do - expect(described_class.verify_expiration(string_payload, options)).to eq(nil) - end - - context 'when leeway is not specified' do - let(:options) { {} } - - it 'used a default leeway of 0' do - expect do - described_class.verify_expiration(payload, options) - end.to raise_error JWT::ExpiredSignature - end - end - end - - context '.verify_iat(payload, options)' do - let(:iat) { Time.now.to_f } - let(:payload) { base_payload.merge('iat' => iat) } - - it 'must allow a valid iat' do - described_class.verify_iat(payload, options) - end - - it 'must ignore configured leeway' do - expect { described_class.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(leeway: 70)) } - .to raise_error(JWT::InvalidIatError) - end - - it 'must properly handle integer times' do - described_class.verify_iat(payload.merge('iat' => Time.now.to_i), options) - end - - it 'must raise JWT::InvalidIatError when the iat value is not Numeric' do - expect do - described_class.verify_iat(payload.merge('iat' => 'not a number'), options) - end.to raise_error JWT::InvalidIatError - end - - it 'must raise JWT::InvalidIatError when the iat value is in the future' do - expect do - described_class.verify_iat(payload.merge('iat' => (iat + 120)), options) - end.to raise_error JWT::InvalidIatError - end - - it 'must not validate if the payload is a string containing iat' do - expect(described_class.verify_iat(string_payload, options)).to eq(nil) - end - end - - context '.verify_iss(payload, options)' do - let(:iss) { 'ruby-jwt-gem' } - let(:payload) { base_payload.merge('iss' => iss) } - - let(:invalid_token) { JWT.encode base_payload, payload[:secret] } - - context 'when iss is a String' do - it 'must raise JWT::InvalidIssuerError when the configured issuer does not match the payload issuer' do - expect do - described_class.verify_iss(payload, options.merge(iss: 'mismatched-issuer')) - end.to raise_error JWT::InvalidIssuerError - end - - it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do - expect do - described_class.verify_iss(base_payload, options.merge(iss: iss)) - end.to raise_error(JWT::InvalidIssuerError, /received /) - end - - it 'must allow a matching issuer to pass' do - described_class.verify_iss(payload, options.merge(iss: iss)) - end - end - context 'when iss is an Array' do - it 'must raise JWT::InvalidIssuerError when no matching issuers in array' do - expect do - described_class.verify_iss(payload, options.merge(iss: %w[first second])) - end.to raise_error JWT::InvalidIssuerError - end - - it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do - expect do - described_class.verify_iss(base_payload, options.merge(iss: %w[first second])) - end.to raise_error(JWT::InvalidIssuerError, /received /) - end - - it 'must allow an array with matching issuer to pass' do - described_class.verify_iss(payload, options.merge(iss: ['first', iss, 'third'])) - end - end - context 'when iss is a RegExp' do - it 'must raise JWT::InvalidIssuerError when the regular expression does not match' do - expect do - described_class.verify_iss(payload, options.merge(iss: /\A(first|second)\z/)) - end.to raise_error JWT::InvalidIssuerError - end - - it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do - expect do - described_class.verify_iss(base_payload, options.merge(iss: /\A(first|second)\z/)) - end.to raise_error(JWT::InvalidIssuerError, /received /) - end - - it 'must allow a regular expression matching the issuer to pass' do - described_class.verify_iss(payload, options.merge(iss: /\A(first|#{iss}|third)\z/)) - end - end - context 'when iss is a Proc' do - it 'must raise JWT::InvalidIssuerError when the proc returns false' do - expect do - described_class.verify_iss(payload, options.merge(iss: ->(iss) { iss && iss.start_with?('first') })) - end.to raise_error JWT::InvalidIssuerError - end - - it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do - expect do - described_class.verify_iss(base_payload, options.merge(iss: ->(iss) { iss && iss.start_with?('first') })) - end.to raise_error(JWT::InvalidIssuerError, /received /) - end - - it 'must allow a proc that returns true to pass' do - described_class.verify_iss(payload, options.merge(iss: ->(iss) { iss && iss.start_with?('ruby') })) - end - end - context 'when iss is a Method instance' do - def issuer_start_with_first?(issuer) - issuer&.start_with?('first') - end - - def issuer_start_with_ruby?(issuer) - issuer&.start_with?('ruby') - end - - it 'must raise JWT::InvalidIssuerError when the method returns false' do - expect do - described_class.verify_iss(payload, options.merge(iss: method(:issuer_start_with_first?))) - end.to raise_error JWT::InvalidIssuerError - end - - it 'must raise JWT::InvalidIssuerError when the payload does not include an issuer' do - expect do - described_class.verify_iss(base_payload, options.merge(iss: method(:issuer_start_with_first?))) - end.to raise_error(JWT::InvalidIssuerError, /received /) - end - - it 'must allow a method that returns true to pass' do - described_class.verify_iss(payload, options.merge(iss: method(:issuer_start_with_ruby?))) - end - end - end - - context '.verify_jti(payload, options)' do - let(:payload) { base_payload.merge('jti' => 'some-random-uuid-or-whatever') } - - it 'must allow any jti when the verfy_jti key in the options is truthy but not a proc' do - described_class.verify_jti(payload, options.merge(verify_jti: true)) - end - - it 'must raise JWT::InvalidJtiError when the jti is missing' do - expect do - described_class.verify_jti(base_payload, options) - end.to raise_error JWT::InvalidJtiError, /missing/i - end - - it 'must raise JWT::InvalidJtiError when the jti is an empty string' do - expect do - described_class.verify_jti(base_payload.merge('jti' => ' '), options) - end.to raise_error JWT::InvalidJtiError, /missing/i - end - - it 'must raise JWT::InvalidJtiError when verify_jti proc returns false' do - expect do - described_class.verify_jti(payload, options.merge(verify_jti: ->(_jti) { false })) - end.to raise_error JWT::InvalidJtiError, /invalid/i - end - - it 'true proc should not raise JWT::InvalidJtiError' do - described_class.verify_jti(payload, options.merge(verify_jti: ->(_jti) { true })) - end - - it 'it should not throw arguement error with 2 args' do - expect do - described_class.verify_jti(payload, options.merge(verify_jti: lambda { |_jti, _pl| - true - })) - end.to_not raise_error - end - it 'should have payload as second param in proc' do - described_class.verify_jti(payload, options.merge(verify_jti: lambda { |_jti, pl| - expect(pl).to eq(payload) - })) - end - end - - context '.verify_not_before(payload, options)' do - let(:payload) { base_payload.merge('nbf' => (Time.now.to_i + 5)) } - - it 'must raise JWT::ImmatureSignature when the nbf in the payload is in the future' do - expect do - described_class.verify_not_before(payload, options) - end.to raise_error JWT::ImmatureSignature - end - - it 'must allow some leeway in the token age when global leeway is configured' do - described_class.verify_not_before(payload, options.merge(leeway: 10)) - end - - it 'must allow some leeway in the token age when nbf_leeway is configured' do - described_class.verify_not_before(payload, options.merge(nbf_leeway: 10)) - end - - it 'must not validate if the payload is a string containing iat' do - expect(described_class.verify_not_before(string_payload, options)).to eq(nil) - end - end - - context '.verify_sub(payload, options)' do - let(:sub) { 'ruby jwt subject' } - - it 'must raise JWT::InvalidSubError when the subjects do not match' do - expect do - described_class.verify_sub(base_payload.merge('sub' => 'not-a-match'), options.merge(sub: sub)) - end.to raise_error JWT::InvalidSubError - end - - it 'must allow a matching sub' do - described_class.verify_sub(base_payload.merge('sub' => sub), options.merge(sub: sub)) - end - end - - context '.verify_claims' do - let(:fail_verifications_options) { { iss: 'mismatched-issuer', aud: 'no-match', sub: 'some subject' } } - let(:fail_verifications_payload) do - { - 'exp' => (Time.now.to_i - 50), - 'jti' => ' ', - 'iss' => 'some-issuer', - 'nbf' => (Time.now.to_i + 50), - 'iat' => 'not a number', - 'sub' => 'not-a-match' - } - end - - %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub].each do |method| - let(:payload) { base_payload.merge(fail_verifications_payload) } - it "must skip verification when #{method} option is set to false" do - expect(described_class.verify_claims(payload, options.merge(method => false))).to be_truthy - end - - it "must raise error when #{method} option is set to true" do - expect do - described_class.verify_claims(payload, options.merge(method => true).merge(fail_verifications_options)) - end.to raise_error JWT::DecodeError - end - end - end - - context '.verify_required_claims(payload, options)' do - it 'must raise JWT::MissingRequiredClaim if a required claim is absent' do - expect do - described_class.verify_required_claims(base_payload, options.merge(required_claims: ['exp'])) - end.to raise_error JWT::MissingRequiredClaim - end - - it 'must verify the claims if all required claims are present' do - payload = base_payload.merge('exp' => (Time.now.to_i + 5), 'custom_claim' => true) - described_class.verify_required_claims(payload, options.merge(required_claims: %w[exp custom_claim])) - end - end -end