jwt 2.2.3 → 2.4.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.
data/lib/jwt/jwk/ec.rb CHANGED
@@ -8,7 +8,7 @@ module JWT
8
8
  extend Forwardable
9
9
  def_delegators :@keypair, :public_key
10
10
 
11
- KTY = 'EC'.freeze
11
+ KTY = 'EC'
12
12
  KTYS = [KTY, OpenSSL::PKey::EC].freeze
13
13
  BINARY = 2
14
14
 
@@ -66,17 +66,17 @@ module JWT
66
66
  crv = 'P-521'
67
67
  x_octets, y_octets = encoded_point.unpack('xa66a66')
68
68
  else
69
- raise Jwt::JWKError, "Unsupported curve '#{ec_keypair.group.curve_name}'"
69
+ raise JWT::JWKError, "Unsupported curve '#{ec_keypair.group.curve_name}'"
70
70
  end
71
71
  [crv, x_octets, y_octets]
72
72
  end
73
73
 
74
74
  def encode_octets(octets)
75
- ::JWT::Base64.url_encode(octets)
75
+ Base64.urlsafe_encode64(octets, padding: false)
76
76
  end
77
77
 
78
78
  def encode_open_ssl_bn(key_part)
79
- ::JWT::Base64.url_encode(key_part.to_s(BINARY))
79
+ Base64.urlsafe_encode64(key_part.to_s(BINARY), padding: false)
80
80
  end
81
81
 
82
82
  class << self
@@ -85,7 +85,7 @@ module JWT
85
85
  # explanation of the relevant parameters.
86
86
 
87
87
  jwk_crv, jwk_x, jwk_y, jwk_d, jwk_kid = jwk_attrs(jwk_data, %i[crv x y d kid])
88
- raise Jwt::JWKError, 'Key format is invalid for EC' unless jwk_crv && jwk_x && jwk_y
88
+ raise JWT::JWKError, 'Key format is invalid for EC' unless jwk_crv && jwk_x && jwk_y
89
89
 
90
90
  new(ec_pkey(jwk_crv, jwk_x, jwk_y, jwk_d), jwk_kid)
91
91
  end
@@ -138,11 +138,11 @@ module JWT
138
138
  end
139
139
 
140
140
  def decode_octets(jwk_data)
141
- ::JWT::Base64.url_decode(jwk_data)
141
+ Base64.urlsafe_decode64(jwk_data)
142
142
  end
143
143
 
144
144
  def decode_open_ssl_bn(jwk_data)
145
- OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
145
+ OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data), BINARY)
146
146
  end
147
147
  end
148
148
  end
data/lib/jwt/jwk/hmac.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module JWT
4
4
  module JWK
5
5
  class HMAC < KeyBase
6
- KTY = 'oct'.freeze
6
+ KTY = 'oct'
7
7
  KTYS = [KTY, String].freeze
8
8
 
9
9
  def initialize(keypair, kid = nil)
@@ -11,6 +11,7 @@ module JWT
11
11
  end
12
12
 
13
13
  def self.inherited(klass)
14
+ super
14
15
  ::JWT::JWK.classes << klass
15
16
  end
16
17
  end
data/lib/jwt/jwk/rsa.rb CHANGED
@@ -4,12 +4,13 @@ module JWT
4
4
  module JWK
5
5
  class RSA < KeyBase
6
6
  BINARY = 2
7
- KTY = 'RSA'.freeze
7
+ KTY = 'RSA'
8
8
  KTYS = [KTY, OpenSSL::PKey::RSA].freeze
9
9
  RSA_KEY_ELEMENTS = %i[n e d p q dp dq qi].freeze
10
10
 
11
11
  def initialize(keypair, kid = nil)
12
12
  raise ArgumentError, 'keypair must be of type OpenSSL::PKey::RSA' unless keypair.is_a?(OpenSSL::PKey::RSA)
13
+
13
14
  super(keypair, kid || generate_kid(keypair.public_key))
14
15
  end
15
16
 
@@ -54,7 +55,7 @@ module JWT
54
55
  end
55
56
 
56
57
  def encode_open_ssl_bn(key_part)
57
- ::JWT::Base64.url_encode(key_part.to_s(BINARY))
58
+ Base64.urlsafe_encode64(key_part.to_s(BINARY), padding: false)
58
59
  end
59
60
 
60
61
  class << self
@@ -107,7 +108,7 @@ module JWT
107
108
  def decode_open_ssl_bn(jwk_data)
108
109
  return nil unless jwk_data
109
110
 
110
- OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
111
+ OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data), BINARY)
111
112
  end
112
113
  end
113
114
  end
data/lib/jwt/jwk.rb CHANGED
@@ -14,10 +14,10 @@ module JWT
14
14
  end.import(jwk_data)
15
15
  end
16
16
 
17
- def create_from(keypair)
17
+ def create_from(keypair, kid = nil)
18
18
  mappings.fetch(keypair.class) do |klass|
19
19
  raise JWT::JWKError, "Cannot create JWK from a #{klass.name}"
20
- end.new(keypair)
20
+ end.new(keypair, kid)
21
21
  end
22
22
 
23
23
  def classes
@@ -36,6 +36,7 @@ module JWT
36
36
  def generate_mappings
37
37
  classes.each_with_object({}) do |klass, hash|
38
38
  next unless klass.const_defined?('KTYS')
39
+
39
40
  Array(klass::KTYS).each do |kty|
40
41
  hash[kty] = klass
41
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JWT
2
4
  # Collection of security methods
3
5
  #
data/lib/jwt/signature.rb CHANGED
@@ -13,7 +13,8 @@ end
13
13
  module JWT
14
14
  # Signature logic for JWT
15
15
  module Signature
16
- extend self
16
+ module_function
17
+
17
18
  ToSign = Struct.new(:algorithm, :msg, :key)
18
19
  ToVerify = Struct.new(:algorithm, :public_key, :signing_input, :signature)
19
20
 
@@ -23,13 +24,8 @@ module JWT
23
24
  end
24
25
 
25
26
  def verify(algorithm, key, signing_input, signature)
26
- return true if algorithm.casecmp('none').zero?
27
-
28
- raise JWT::DecodeError, 'No verification key available' unless key
29
-
30
27
  algo, code = Algos.find(algorithm)
31
- verified = algo.verify(ToVerify.new(code, key, signing_input, signature))
32
- raise(JWT::VerificationError, 'Signature verification raised') unless verified
28
+ algo.verify(ToVerify.new(code, key, signing_input, signature))
33
29
  rescue OpenSSL::PKey::PKeyError
34
30
  raise JWT::VerificationError, 'Signature verification raised'
35
31
  ensure
data/lib/jwt/verify.rb CHANGED
@@ -10,7 +10,7 @@ module JWT
10
10
  }.freeze
11
11
 
12
12
  class << self
13
- %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub].each do |method_name|
13
+ %w[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub verify_required_claims].each do |method_name|
14
14
  define_method method_name do |payload, options|
15
15
  new(payload, options).send(method_name)
16
16
  end
@@ -19,6 +19,7 @@ module JWT
19
19
  def verify_claims(payload, options)
20
20
  options.each do |key, val|
21
21
  next unless key.to_s =~ /verify/
22
+
22
23
  Verify.send(key, payload, options) if val
23
24
  end
24
25
  end
@@ -53,9 +54,14 @@ module JWT
53
54
 
54
55
  iss = @payload['iss']
55
56
 
56
- return if Array(options_iss).map(&:to_s).include?(iss.to_s)
57
+ options_iss = Array(options_iss).map { |item| item.is_a?(Symbol) ? item.to_s : item }
57
58
 
58
- raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}")
59
+ case iss
60
+ when *options_iss
61
+ nil
62
+ else
63
+ raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{iss || '<none>'}")
64
+ end
59
65
  end
60
66
 
61
67
  def verify_jti
@@ -77,10 +83,19 @@ module JWT
77
83
 
78
84
  def verify_sub
79
85
  return unless (options_sub = @options[:sub])
86
+
80
87
  sub = @payload['sub']
81
88
  raise(JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{sub || '<none>'}") unless sub.to_s == options_sub.to_s
82
89
  end
83
90
 
91
+ def verify_required_claims
92
+ return unless (options_required_claims = @options[:required_claims])
93
+
94
+ options_required_claims.each do |required_claim|
95
+ raise(JWT::MissingRequiredClaim, "Missing required claim #{required_claim}") unless @payload.include?(required_claim)
96
+ end
97
+ end
98
+
84
99
  private
85
100
 
86
101
  def global_leeway
data/lib/jwt/version.rb CHANGED
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  # Moments version builder module
@@ -12,9 +11,9 @@ module JWT
12
11
  # major version
13
12
  MAJOR = 2
14
13
  # minor version
15
- MINOR = 2
14
+ MINOR = 4
16
15
  # tiny version
17
- TINY = 3
16
+ TINY = 0
18
17
  # alpha, beta, etc. tag
19
18
  PRE = nil
20
19
 
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'jwt/error'
5
+
6
+ module JWT
7
+ # If the x5c header certificate chain can be validated by trusted root
8
+ # certificates, and none of the certificates are revoked, returns the public
9
+ # key from the first certificate.
10
+ # See https://tools.ietf.org/html/rfc7515#section-4.1.6
11
+ class X5cKeyFinder
12
+ def initialize(root_certificates, crls = nil)
13
+ raise(ArgumentError, 'Root certificates must be specified') unless root_certificates
14
+
15
+ @store = build_store(root_certificates, crls)
16
+ end
17
+
18
+ def from(x5c_header_or_certificates)
19
+ signing_certificate, *certificate_chain = parse_certificates(x5c_header_or_certificates)
20
+ store_context = OpenSSL::X509::StoreContext.new(@store, signing_certificate, certificate_chain)
21
+
22
+ if store_context.verify
23
+ signing_certificate.public_key
24
+ else
25
+ error = "Certificate verification failed: #{store_context.error_string}."
26
+ if (current_cert = store_context.current_cert)
27
+ error = "#{error} Certificate subject: #{current_cert.subject}."
28
+ end
29
+
30
+ raise(JWT::VerificationError, error)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def build_store(root_certificates, crls)
37
+ store = OpenSSL::X509::Store.new
38
+ store.purpose = OpenSSL::X509::PURPOSE_ANY
39
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
40
+ root_certificates.each { |certificate| store.add_cert(certificate) }
41
+ crls&.each { |crl| store.add_crl(crl) }
42
+ store
43
+ end
44
+
45
+ def parse_certificates(x5c_header_or_certificates)
46
+ if x5c_header_or_certificates.all? { |obj| obj.is_a?(OpenSSL::X509::Certificate) }
47
+ x5c_header_or_certificates
48
+ else
49
+ x5c_header_or_certificates.map do |encoded|
50
+ OpenSSL::X509::Certificate.new(::Base64.strict_decode64(encoded))
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/jwt.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'jwt/base64'
3
+ require 'base64'
4
4
  require 'jwt/json'
5
5
  require 'jwt/decode'
6
6
  require 'jwt/default_options'
data/ruby-jwt.gemspec CHANGED
@@ -1,4 +1,6 @@
1
- lib = File.expand_path('../lib/', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'jwt/version'
4
6
 
@@ -13,7 +15,11 @@ Gem::Specification.new do |spec|
13
15
  spec.description = 'A pure ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.'
14
16
  spec.homepage = 'https://github.com/jwt/ruby-jwt'
15
17
  spec.license = 'MIT'
16
- spec.required_ruby_version = '>= 2.1'
18
+ spec.required_ruby_version = '>= 2.5'
19
+ spec.metadata = {
20
+ 'bug_tracker_uri' => 'https://github.com/jwt/ruby-jwt/issues',
21
+ 'changelog_uri' => "https://github.com/jwt/ruby-jwt/blob/v#{JWT.gem_version}/CHANGELOG.md"
22
+ }
17
23
 
18
24
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|gemfiles|coverage|bin)/}) }
19
25
  spec.executables = []
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.2.3
4
+ version: 2.4.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: 2021-04-19 00:00:00.000000000 Z
11
+ date: 2022-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -87,6 +87,8 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".codeclimate.yml"
91
+ - ".github/workflows/coverage.yml"
90
92
  - ".github/workflows/test.yml"
91
93
  - ".gitignore"
92
94
  - ".rspec"
@@ -96,6 +98,7 @@ files:
96
98
  - AUTHORS
97
99
  - Appraisals
98
100
  - CHANGELOG.md
101
+ - CODE_OF_CONDUCT.md
99
102
  - Gemfile
100
103
  - LICENSE
101
104
  - README.md
@@ -109,7 +112,6 @@ files:
109
112
  - lib/jwt/algos/ps.rb
110
113
  - lib/jwt/algos/rsa.rb
111
114
  - lib/jwt/algos/unsupported.rb
112
- - lib/jwt/base64.rb
113
115
  - lib/jwt/claims_validator.rb
114
116
  - lib/jwt/decode.rb
115
117
  - lib/jwt/default_options.rb
@@ -126,11 +128,14 @@ files:
126
128
  - lib/jwt/signature.rb
127
129
  - lib/jwt/verify.rb
128
130
  - lib/jwt/version.rb
131
+ - lib/jwt/x5c_key_finder.rb
129
132
  - ruby-jwt.gemspec
130
133
  homepage: https://github.com/jwt/ruby-jwt
131
134
  licenses:
132
135
  - MIT
133
- metadata: {}
136
+ metadata:
137
+ bug_tracker_uri: https://github.com/jwt/ruby-jwt/issues
138
+ changelog_uri: https://github.com/jwt/ruby-jwt/blob/v2.4.0/CHANGELOG.md
134
139
  post_install_message:
135
140
  rdoc_options: []
136
141
  require_paths:
@@ -139,14 +144,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
144
  requirements:
140
145
  - - ">="
141
146
  - !ruby/object:Gem::Version
142
- version: '2.1'
147
+ version: '2.5'
143
148
  required_rubygems_version: !ruby/object:Gem::Requirement
144
149
  requirements:
145
150
  - - ">="
146
151
  - !ruby/object:Gem::Version
147
152
  version: '0'
148
153
  requirements: []
149
- rubygems_version: 3.2.16
154
+ rubygems_version: 3.3.7
150
155
  signing_key:
151
156
  specification_version: 4
152
157
  summary: JSON Web Token implementation in Ruby
data/lib/jwt/base64.rb DELETED
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base64'
4
-
5
- module JWT
6
- # Base64 helpers
7
- class Base64
8
- class << self
9
- def url_encode(str)
10
- ::Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
11
- end
12
-
13
- def url_decode(str)
14
- str += '=' * (4 - str.length.modulo(4))
15
- ::Base64.decode64(str.tr('-_', '+/'))
16
- end
17
- end
18
- end
19
- end