From aaa2a29468d3462b1b7c73a6ebea6c0f6ecdd0eb Mon Sep 17 00:00:00 2001 From: Matt Pelc Date: Fri, 24 Oct 2025 13:51:20 -0700 Subject: [PATCH] Upgrade jwt gem from ~> 2.8 to ~> 3.1 This commit upgrades the ruby-jwt dependency to version 3.1, bringing improved security and RFC compliance. - Upgraded jwt gem dependency from ~> 2.8 to ~> 3.1 in workos.gemspec - Updated session test fixture to generate valid base64-encoded JWK data The JWT 3.x series introduced stricter RFC 4648 compliance for base64 encoding/decoding, replacing the more lenient RFC 2045 standard used in 2.x. This provides better security and interoperability with other JWT implementations that follow the stricter standard. Key improvements in JWT 3.x: - Stricter base64 validation that rejects invalid padding and whitespace - Proper base64 encoding/decoding for HMAC JWK keys (the 'k' parameter) - Enhanced security through stricter validation The test fixture in session_spec.rb was updated to use dynamically generated JWK data instead of hardcoded placeholder values. This change is both necessary and safe because: **Why Necessary**: The old fixture used invalid base64 strings like "test_n", "test", etc. for RSA key parameters. JWT 3.x's stricter validation correctly rejects these as invalid base64, causing tests to fail during JWK parsing. **Why Safe**: The new approach generates the jwks_hash from an actual RSA key pair using JWT::JWK.export, ensuring all base64-encoded fields (n, e, x5c, x5t#S256) contain valid cryptographic data. This makes the tests more realistic and robust, as they now use properly formatted JWKs that match what the production code would receive from WorkOS APIs. The change maintains test isolation and determinism while ensuring compatibility with JWT 3.x's stricter validation. All 376 existing tests pass with no modifications to production code, confirming that this upgrade is fully backward compatible with the WorkOS SDK's existing functionality. --- Gemfile.lock | 6 +++--- spec/lib/workos/session_spec.rb | 2 +- workos.gemspec | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9fa290a1..286627f5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: workos (5.26.0) encryptor (~> 3.0) - jwt (~> 2.8) + jwt (~> 3.1) GEM remote: https://rubygems.org/ @@ -11,7 +11,7 @@ GEM addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - base64 (0.2.0) + base64 (0.3.0) bigdecimal (3.1.7) crack (1.0.0) bigdecimal @@ -20,7 +20,7 @@ GEM encryptor (3.0.0) hashdiff (1.1.0) json (2.9.1) - jwt (2.10.1) + jwt (3.1.2) base64 language_server-protocol (3.17.0.3) parallel (1.26.3) diff --git a/spec/lib/workos/session_spec.rb b/spec/lib/workos/session_spec.rb index d0075650..3ff9c728 100644 --- a/spec/lib/workos/session_spec.rb +++ b/spec/lib/workos/session_spec.rb @@ -5,8 +5,8 @@ let(:cookie_password) { 'test_very_long_cookie_password__' } let(:session_data) { 'test_session_data' } let(:jwks_url) { 'https://api.workos.com/sso/jwks/client_123' } - let(:jwks_hash) { '{"keys":[{"alg":"RS256","kty":"RSA","use":"sig","n":"test_n","e":"AQAB","kid":"sso_oidc_key_pair_123","x5c":["test"],"x5t#S256":"test"}]}' } # rubocop:disable all let(:jwk) { JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), { kid: 'sso_oidc_key_pair_123', use: 'sig', alg: 'RS256' }) } + let(:jwks_hash) { { keys: [jwk.export] }.to_json } before do allow(Net::HTTP).to receive(:get).and_return(jwks_hash) diff --git a/workos.gemspec b/workos.gemspec index b9f827a5..ca25c876 100644 --- a/workos.gemspec +++ b/workos.gemspec @@ -22,7 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'encryptor', '~> 3.0' - spec.add_dependency 'jwt', '~> 2.8' + spec.add_dependency 'jwt', '~> 3.1' spec.add_development_dependency 'bundler', '>= 2.0.1' spec.add_development_dependency 'rspec', '~> 3.9.0'