From d5dd4e56de2bde8ff628683abdce347b20445160 Mon Sep 17 00:00:00 2001 From: Joakim Antman Date: Sun, 16 Jun 2024 22:40:26 +0300 Subject: [PATCH 1/2] Print deprecation warnings only if token validation succeeds --- CHANGELOG.md | 3 ++- lib/jwt.rb | 4 +++- lib/jwt/base64.rb | 2 +- lib/jwt/deprecations.rb | 29 ++++++++++++++++++++++++----- spec/jwt/jwt_spec.rb | 18 ++++++++++++++++++ 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40239a10..d3b86376 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,9 @@ **Fixes and enhancements:** +- Print deprecation warnings only on when token decoding succeeds [#600](https://github.com/jwt/ruby-jwt/pull/600) ([@anakinj](https://github.com/anakinj)) - Your contribution here -- + ## [v2.8.1](https://github.com/jwt/ruby-jwt/tree/v2.8.1) (2024-02-29) [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.8.0...v2.8.1) diff --git a/lib/jwt.rb b/lib/jwt.rb index 19b987ce..235d3a9f 100644 --- a/lib/jwt.rb +++ b/lib/jwt.rb @@ -27,6 +27,8 @@ def encode(payload, key, algorithm = 'HS256', header_fields = {}) end def decode(jwt, key = nil, verify = true, options = {}, &keyfinder) # rubocop:disable Style/OptionalBooleanParameter - Decode.new(jwt, key, verify, configuration.decode.to_h.merge(options), &keyfinder).decode_segments + Deprecations.context do + Decode.new(jwt, key, verify, configuration.decode.to_h.merge(options), &keyfinder).decode_segments + end end end diff --git a/lib/jwt/base64.rb b/lib/jwt/base64.rb index e6ee0622..20f56b6c 100644 --- a/lib/jwt/base64.rb +++ b/lib/jwt/base64.rb @@ -20,7 +20,7 @@ def url_decode(str) raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding loose_urlsafe_decode64(str).tap do - Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt') + Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt', only_if_valid: true) end end diff --git a/lib/jwt/deprecations.rb b/lib/jwt/deprecations.rb index 92f0e6cf..b77540a1 100644 --- a/lib/jwt/deprecations.rb +++ b/lib/jwt/deprecations.rb @@ -4,15 +4,34 @@ module JWT # Deprecations module to handle deprecation warnings in the gem module Deprecations class << self - def warning(message) + def context + yield.tap { emit_warnings } + end + + def warning(message, only_if_valid: false) + method_name = only_if_valid ? :store : :warn case JWT.configuration.deprecation_warnings - when :warn - warn("[DEPRECATION WARNING] #{message}") when :once return if record_warned(message) - - warn("[DEPRECATION WARNING] #{message}") + when :warn + # noop + else + return end + + send(method_name, "[DEPRECATION WARNING] #{message}") + end + + def store(message) + (Thread.current[:jwt_warning_store] ||= []) << message + end + + def emit_warnings + return if Thread.current[:jwt_warning_store].nil? + + Thread.current[:jwt_warning_store].each { |warning| warn(warning) } + + Thread.current[:jwt_warning_store] = nil end private diff --git a/spec/jwt/jwt_spec.rb b/spec/jwt/jwt_spec.rb index 796dabb8..da1c01cd 100644 --- a/spec/jwt/jwt_spec.rb +++ b/spec/jwt/jwt_spec.rb @@ -944,4 +944,22 @@ def valid_alg?(alg) end end end + + context 'when invalid token is valid loose base64' do + it 'does not output deprecations warnings' do + expect { + begin + JWT.decode("#{JWT.encode('a', 'b')} 9", 'b') + rescue JWT::VerificationError + nil + end + }.not_to output(/DEPRECATION/).to_stderr + end + end + + context 'when valid token is invalid strict base64' do + it 'does outputs deprecation warning' do + expect { JWT.decode("#{JWT.encode('a', 'b')} ", 'b') }.to output(/DEPRECATION/).to_stderr + end + end end From 683ac1b407cbb7923388b416a22c318959074fd8 Mon Sep 17 00:00:00 2001 From: Joakim Antman Date: Mon, 17 Jun 2024 01:22:30 +0300 Subject: [PATCH 2/2] Always empty warnings --- lib/jwt/deprecations.rb | 4 ++-- spec/jwt/jwt_spec.rb | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/jwt/deprecations.rb b/lib/jwt/deprecations.rb index b77540a1..9f516a2d 100644 --- a/lib/jwt/deprecations.rb +++ b/lib/jwt/deprecations.rb @@ -6,6 +6,8 @@ module Deprecations class << self def context yield.tap { emit_warnings } + ensure + Thread.current[:jwt_warning_store] = nil end def warning(message, only_if_valid: false) @@ -30,8 +32,6 @@ def emit_warnings return if Thread.current[:jwt_warning_store].nil? Thread.current[:jwt_warning_store].each { |warning| warn(warning) } - - Thread.current[:jwt_warning_store] = nil end private diff --git a/spec/jwt/jwt_spec.rb b/spec/jwt/jwt_spec.rb index da1c01cd..5dcd05b5 100644 --- a/spec/jwt/jwt_spec.rb +++ b/spec/jwt/jwt_spec.rb @@ -957,9 +957,23 @@ def valid_alg?(alg) end end - context 'when valid token is invalid strict base64' do + context 'when valid token is invalid strict base64 and decoded with the correct key' do it 'does outputs deprecation warning' do - expect { JWT.decode("#{JWT.encode('a', 'b')} ", 'b') }.to output(/DEPRECATION/).to_stderr + expect { JWT.decode("#{JWT.encode('payload', 'key')} ", 'key') }.to output(/DEPRECATION/).to_stderr + end + end + + context 'when valid token is invalid strict base64 and decoded with the incorrect key' do + it 'does not output deprecation warning, even when decoded with the correct key' do + token = JWT.encode('payload', 'key') + expect { + begin + JWT.decode("#{token} ", 'incorrect') + rescue JWT::VerificationError + nil + end + JWT.decode(token, 'key') + }.not_to output(/DEPRECATION/).to_stderr end end end