jwt 2.2.3 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
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