jwt 2.9.2 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -16
  3. data/README.md +153 -83
  4. data/lib/jwt/base64.rb +3 -0
  5. data/lib/jwt/claims/audience.rb +10 -0
  6. data/lib/jwt/claims/crit.rb +35 -0
  7. data/lib/jwt/claims/decode_verifier.rb +3 -3
  8. data/lib/jwt/claims/expiration.rb +10 -0
  9. data/lib/jwt/claims/issued_at.rb +7 -0
  10. data/lib/jwt/claims/issuer.rb +10 -0
  11. data/lib/jwt/claims/jwt_id.rb +10 -0
  12. data/lib/jwt/claims/not_before.rb +10 -0
  13. data/lib/jwt/claims/numeric.rb +22 -0
  14. data/lib/jwt/claims/required.rb +10 -0
  15. data/lib/jwt/claims/subject.rb +10 -0
  16. data/lib/jwt/claims/verification_methods.rb +20 -0
  17. data/lib/jwt/claims/verifier.rb +6 -7
  18. data/lib/jwt/claims.rb +6 -14
  19. data/lib/jwt/claims_validator.rb +5 -2
  20. data/lib/jwt/configuration/container.rb +20 -0
  21. data/lib/jwt/configuration/decode_configuration.rb +24 -0
  22. data/lib/jwt/configuration/jwk_configuration.rb +1 -0
  23. data/lib/jwt/configuration.rb +8 -0
  24. data/lib/jwt/decode.rb +28 -68
  25. data/lib/jwt/deprecations.rb +1 -0
  26. data/lib/jwt/encode.rb +17 -56
  27. data/lib/jwt/encoded_token.rb +139 -0
  28. data/lib/jwt/error.rb +34 -0
  29. data/lib/jwt/json.rb +1 -1
  30. data/lib/jwt/jwa/compat.rb +3 -0
  31. data/lib/jwt/jwa/ecdsa.rb +3 -6
  32. data/lib/jwt/jwa/eddsa.rb +7 -6
  33. data/lib/jwt/jwa/hmac.rb +2 -3
  34. data/lib/jwt/jwa/hmac_rbnacl.rb +1 -0
  35. data/lib/jwt/jwa/hmac_rbnacl_fixed.rb +1 -0
  36. data/lib/jwt/jwa/none.rb +1 -0
  37. data/lib/jwt/jwa/ps.rb +2 -3
  38. data/lib/jwt/jwa/rsa.rb +2 -3
  39. data/lib/jwt/jwa/signing_algorithm.rb +3 -0
  40. data/lib/jwt/jwa/unsupported.rb +1 -0
  41. data/lib/jwt/jwa/wrapper.rb +1 -0
  42. data/lib/jwt/jwa.rb +11 -3
  43. data/lib/jwt/jwk/ec.rb +2 -3
  44. data/lib/jwt/jwk/hmac.rb +2 -3
  45. data/lib/jwt/jwk/key_base.rb +1 -0
  46. data/lib/jwt/jwk/key_finder.rb +1 -0
  47. data/lib/jwt/jwk/kid_as_key_digest.rb +1 -0
  48. data/lib/jwt/jwk/okp_rbnacl.rb +3 -4
  49. data/lib/jwt/jwk/rsa.rb +2 -3
  50. data/lib/jwt/jwk/set.rb +2 -0
  51. data/lib/jwt/jwk.rb +1 -0
  52. data/lib/jwt/token.rb +112 -0
  53. data/lib/jwt/verify.rb +7 -0
  54. data/lib/jwt/version.rb +33 -10
  55. data/lib/jwt.rb +16 -0
  56. metadata +8 -4
data/lib/jwt/token.rb ADDED
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ # Represents a JWT token
5
+ #
6
+ # Basic token signed using the HS256 algorithm:
7
+ #
8
+ # token = JWT::Token.new(payload: {pay: 'load'})
9
+ # token.sign!(algorithm: 'HS256', key: 'secret')
10
+ # token.jwt # => eyJhb....
11
+ #
12
+ # Custom headers will be combined with generated headers:
13
+ # token = JWT::Token.new(payload: {pay: 'load'}, header: {custom: "value"})
14
+ # token.sign!(algorithm: 'HS256', key: 'secret')
15
+ # token.header # => {"custom"=>"value", "alg"=>"HS256"}
16
+ #
17
+ class Token
18
+ include Claims::VerificationMethods
19
+
20
+ # Initializes a new Token instance.
21
+ #
22
+ # @param header [Hash] the header of the JWT token.
23
+ # @param payload [Hash] the payload of the JWT token.
24
+ def initialize(payload:, header: {})
25
+ @header = header&.transform_keys(&:to_s)
26
+ @payload = payload
27
+ end
28
+
29
+ # Returns the decoded signature of the JWT token.
30
+ #
31
+ # @return [String] the decoded signature of the JWT token.
32
+ def signature
33
+ @signature ||= ::JWT::Base64.url_decode(encoded_signature || '')
34
+ end
35
+
36
+ # Returns the encoded signature of the JWT token.
37
+ #
38
+ # @return [String] the encoded signature of the JWT token.
39
+ def encoded_signature
40
+ @encoded_signature ||= ::JWT::Base64.url_encode(signature)
41
+ end
42
+
43
+ # Returns the decoded header of the JWT token.
44
+ #
45
+ # @return [Hash] the header of the JWT token.
46
+ attr_reader :header
47
+
48
+ # Returns the encoded header of the JWT token.
49
+ #
50
+ # @return [String] the encoded header of the JWT token.
51
+ def encoded_header
52
+ @encoded_header ||= ::JWT::Base64.url_encode(JWT::JSON.generate(header))
53
+ end
54
+
55
+ # Returns the payload of the JWT token.
56
+ #
57
+ # @return [Hash] the payload of the JWT token.
58
+ attr_reader :payload
59
+
60
+ # Returns the encoded payload of the JWT token.
61
+ #
62
+ # @return [String] the encoded payload of the JWT token.
63
+ def encoded_payload
64
+ @encoded_payload ||= ::JWT::Base64.url_encode(JWT::JSON.generate(payload))
65
+ end
66
+
67
+ # Returns the signing input of the JWT token.
68
+ #
69
+ # @return [String] the signing input of the JWT token.
70
+ def signing_input
71
+ @signing_input ||= [encoded_header, encoded_payload].join('.')
72
+ end
73
+
74
+ # Returns the JWT token as a string.
75
+ #
76
+ # @return [String] the JWT token as a string.
77
+ # @raise [JWT::EncodeError] if the token is not signed or other encoding issues
78
+ def jwt
79
+ @jwt ||= (@signature && [encoded_header, @detached_payload ? '' : encoded_payload, encoded_signature].join('.')) || raise(::JWT::EncodeError, 'Token is not signed')
80
+ end
81
+
82
+ # Detaches the payload according to https://datatracker.ietf.org/doc/html/rfc7515#appendix-F
83
+ #
84
+ def detach_payload!
85
+ @detached_payload = true
86
+
87
+ nil
88
+ end
89
+
90
+ # Signs the JWT token.
91
+ #
92
+ # @param algorithm [String, Object] the algorithm to use for signing.
93
+ # @param key [String] the key to use for signing.
94
+ # @return [void]
95
+ # @raise [JWT::EncodeError] if the token is already signed or other problems when signing
96
+ def sign!(algorithm:, key:)
97
+ raise ::JWT::EncodeError, 'Token already signed' if @signature
98
+
99
+ JWA.resolve(algorithm).tap do |algo|
100
+ header.merge!(algo.header)
101
+ @signature = algo.sign(data: signing_input, signing_key: key)
102
+ end
103
+
104
+ nil
105
+ end
106
+
107
+ # Returns the JWT token as a string.
108
+ #
109
+ # @return [String] the JWT token as a string.
110
+ alias to_s jwt
111
+ end
112
+ end
data/lib/jwt/verify.rb CHANGED
@@ -3,10 +3,12 @@
3
3
  require_relative 'error'
4
4
 
5
5
  module JWT
6
+ # @deprecated This class is deprecated and will be removed in the next major version of ruby-jwt.
6
7
  class Verify
7
8
  DEFAULTS = { leeway: 0 }.freeze
8
9
  METHODS = %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub verify_required_claims].freeze
9
10
 
11
+ private_constant(:DEFAULTS, :METHODS)
10
12
  class << self
11
13
  METHODS.each do |method_name|
12
14
  define_method(method_name) do |payload, options|
@@ -14,12 +16,17 @@ module JWT
14
16
  end
15
17
  end
16
18
 
19
+ # @deprecated This method is deprecated and will be removed in the next major version of ruby-jwt.
17
20
  def verify_claims(payload, options)
21
+ Deprecations.warning('The ::JWT::Verify.verify_claims method is deprecated and will be removed in the next major version of ruby-jwt')
18
22
  ::JWT::Claims.verify!(payload, options)
23
+ true
19
24
  end
20
25
  end
21
26
 
27
+ # @deprecated This class is deprecated and will be removed in the next major version of ruby-jwt.
22
28
  def initialize(payload, options)
29
+ Deprecations.warning('The ::JWT::Verify class is deprecated and will be removed in the next major version of ruby-jwt')
23
30
  @payload = payload
24
31
  @options = DEFAULTS.merge(options)
25
32
  end
data/lib/jwt/version.rb CHANGED
@@ -1,44 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Moments version builder module
3
+ # JSON Web Token implementation
4
+ #
5
+ # Should be up to date with the latest spec:
6
+ # https://tools.ietf.org/html/rfc7519
4
7
  module JWT
8
+ # Returns the gem version of the JWT library.
9
+ #
10
+ # @return [Gem::Version] the gem version.
5
11
  def self.gem_version
6
- Gem::Version.new VERSION::STRING
12
+ Gem::Version.new(VERSION::STRING)
7
13
  end
8
14
 
9
- # Moments version builder module
15
+ # @api private
10
16
  module VERSION
11
- # major version
12
17
  MAJOR = 2
13
- # minor version
14
- MINOR = 9
15
- # tiny version
16
- TINY = 2
17
- # alpha, beta, etc. tag
18
+ MINOR = 10
19
+ TINY = 0
18
20
  PRE = nil
19
21
 
20
- # Build version string
21
22
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
23
+
24
+ private_constant(:MAJOR, :MINOR, :TINY, :PRE)
22
25
  end
23
26
 
27
+ # Checks if the OpenSSL version is 3 or greater.
28
+ #
29
+ # @return [Boolean] true if OpenSSL version is 3 or greater, false otherwise.
30
+ # @api private
24
31
  def self.openssl_3?
25
32
  return false if OpenSSL::OPENSSL_VERSION.include?('LibreSSL')
26
33
 
27
34
  true if 3 * 0x10000000 <= OpenSSL::OPENSSL_VERSION_NUMBER
28
35
  end
29
36
 
37
+ # Checks if the RbNaCl library is defined.
38
+ #
39
+ # @return [Boolean] true if RbNaCl is defined, false otherwise.
40
+ # @api private
30
41
  def self.rbnacl?
31
42
  defined?(::RbNaCl)
32
43
  end
33
44
 
45
+ # Checks if the RbNaCl library version is 6.0.0 or greater.
46
+ #
47
+ # @return [Boolean] true if RbNaCl version is 6.0.0 or greater, false otherwise.
48
+ # @api private
34
49
  def self.rbnacl_6_or_greater?
35
50
  rbnacl? && ::Gem::Version.new(::RbNaCl::VERSION) >= ::Gem::Version.new('6.0.0')
36
51
  end
37
52
 
53
+ # Checks if there is an OpenSSL 3 HMAC empty key regression.
54
+ #
55
+ # @return [Boolean] true if there is an OpenSSL 3 HMAC empty key regression, false otherwise.
56
+ # @api private
38
57
  def self.openssl_3_hmac_empty_key_regression?
39
58
  openssl_3? && openssl_version <= ::Gem::Version.new('3.0.0')
40
59
  end
41
60
 
61
+ # Returns the OpenSSL version.
62
+ #
63
+ # @return [Gem::Version] the OpenSSL version.
64
+ # @api private
42
65
  def self.openssl_version
43
66
  @openssl_version ||= ::Gem::Version.new(OpenSSL::VERSION)
44
67
  end
data/lib/jwt.rb CHANGED
@@ -10,6 +10,8 @@ require 'jwt/encode'
10
10
  require 'jwt/error'
11
11
  require 'jwt/jwk'
12
12
  require 'jwt/claims'
13
+ require 'jwt/encoded_token'
14
+ require 'jwt/token'
13
15
 
14
16
  require 'jwt/claims_validator'
15
17
  require 'jwt/verify'
@@ -23,6 +25,13 @@ module JWT
23
25
 
24
26
  module_function
25
27
 
28
+ # Encodes a payload into a JWT.
29
+ #
30
+ # @param payload [Hash] the payload to encode.
31
+ # @param key [String] the key used to sign the JWT.
32
+ # @param algorithm [String] the algorithm used to sign the JWT.
33
+ # @param header_fields [Hash] additional headers to include in the JWT.
34
+ # @return [String] the encoded JWT.
26
35
  def encode(payload, key, algorithm = 'HS256', header_fields = {})
27
36
  Encode.new(payload: payload,
28
37
  key: key,
@@ -30,6 +39,13 @@ module JWT
30
39
  headers: header_fields).segments
31
40
  end
32
41
 
42
+ # Decodes a JWT to extract the payload and header
43
+ #
44
+ # @param jwt [String] the JWT to decode.
45
+ # @param key [String] the key used to verify the JWT.
46
+ # @param verify [Boolean] whether to verify the JWT signature.
47
+ # @param options [Hash] additional options for decoding.
48
+ # @return [Array<Hash>] the decoded payload and headers.
33
49
  def decode(jwt, key = nil, verify = true, options = {}, &keyfinder) # rubocop:disable Style/OptionalBooleanParameter
34
50
  Deprecations.context do
35
51
  Decode.new(jwt, key, verify, configuration.decode.to_h.merge(options), &keyfinder).decode_segments
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.2
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Rudat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-03 00:00:00.000000000 Z
11
+ date: 2024-12-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -125,6 +125,7 @@ files:
125
125
  - lib/jwt/base64.rb
126
126
  - lib/jwt/claims.rb
127
127
  - lib/jwt/claims/audience.rb
128
+ - lib/jwt/claims/crit.rb
128
129
  - lib/jwt/claims/decode_verifier.rb
129
130
  - lib/jwt/claims/expiration.rb
130
131
  - lib/jwt/claims/issued_at.rb
@@ -134,6 +135,7 @@ files:
134
135
  - lib/jwt/claims/numeric.rb
135
136
  - lib/jwt/claims/required.rb
136
137
  - lib/jwt/claims/subject.rb
138
+ - lib/jwt/claims/verification_methods.rb
137
139
  - lib/jwt/claims/verifier.rb
138
140
  - lib/jwt/claims_validator.rb
139
141
  - lib/jwt/configuration.rb
@@ -143,6 +145,7 @@ files:
143
145
  - lib/jwt/decode.rb
144
146
  - lib/jwt/deprecations.rb
145
147
  - lib/jwt/encode.rb
148
+ - lib/jwt/encoded_token.rb
146
149
  - lib/jwt/error.rb
147
150
  - lib/jwt/json.rb
148
151
  - lib/jwt/jwa.rb
@@ -168,6 +171,7 @@ files:
168
171
  - lib/jwt/jwk/rsa.rb
169
172
  - lib/jwt/jwk/set.rb
170
173
  - lib/jwt/jwk/thumbprint.rb
174
+ - lib/jwt/token.rb
171
175
  - lib/jwt/verify.rb
172
176
  - lib/jwt/version.rb
173
177
  - lib/jwt/x5c_key_finder.rb
@@ -177,7 +181,7 @@ licenses:
177
181
  - MIT
178
182
  metadata:
179
183
  bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
180
- changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.9.2/CHANGELOG.md
184
+ changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.10.0/CHANGELOG.md
181
185
  rubygems_mfa_required: 'true'
182
186
  post_install_message:
183
187
  rdoc_options: []
@@ -194,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
198
  - !ruby/object:Gem::Version
195
199
  version: '0'
196
200
  requirements: []
197
- rubygems_version: 3.5.16
201
+ rubygems_version: 3.5.22
198
202
  signing_key:
199
203
  specification_version: 4
200
204
  summary: JSON Web Token implementation in Ruby