jwt 1.5.0 → 2.5.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 +5 -5
  2. data/.codeclimate.yml +8 -0
  3. data/.github/workflows/coverage.yml +27 -0
  4. data/.github/workflows/test.yml +67 -0
  5. data/.gitignore +13 -0
  6. data/.reek.yml +22 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +67 -0
  9. data/.sourcelevel.yml +17 -0
  10. data/AUTHORS +119 -0
  11. data/Appraisals +13 -0
  12. data/CHANGELOG.md +786 -0
  13. data/CODE_OF_CONDUCT.md +84 -0
  14. data/CONTRIBUTING.md +99 -0
  15. data/Gemfile +7 -0
  16. data/LICENSE +7 -0
  17. data/README.md +639 -0
  18. data/Rakefile +13 -14
  19. data/lib/jwt/algos/ecdsa.rb +64 -0
  20. data/lib/jwt/algos/eddsa.rb +35 -0
  21. data/lib/jwt/algos/hmac.rb +36 -0
  22. data/lib/jwt/algos/none.rb +17 -0
  23. data/lib/jwt/algos/ps.rb +43 -0
  24. data/lib/jwt/algos/rsa.rb +22 -0
  25. data/lib/jwt/algos/unsupported.rb +19 -0
  26. data/lib/jwt/algos.rb +44 -0
  27. data/lib/jwt/base64.rb +19 -0
  28. data/lib/jwt/claims_validator.rb +37 -0
  29. data/lib/jwt/configuration/container.rb +21 -0
  30. data/lib/jwt/configuration/decode_configuration.rb +46 -0
  31. data/lib/jwt/configuration/jwk_configuration.rb +27 -0
  32. data/lib/jwt/configuration.rb +15 -0
  33. data/lib/jwt/decode.rb +145 -0
  34. data/lib/jwt/encode.rb +69 -0
  35. data/lib/jwt/error.rb +22 -0
  36. data/lib/jwt/json.rb +10 -22
  37. data/lib/jwt/jwk/ec.rb +199 -0
  38. data/lib/jwt/jwk/hmac.rb +67 -0
  39. data/lib/jwt/jwk/key_base.rb +35 -0
  40. data/lib/jwt/jwk/key_finder.rb +62 -0
  41. data/lib/jwt/jwk/kid_as_key_digest.rb +15 -0
  42. data/lib/jwt/jwk/rsa.rb +138 -0
  43. data/lib/jwt/jwk/thumbprint.rb +26 -0
  44. data/lib/jwt/jwk.rb +52 -0
  45. data/lib/jwt/security_utils.rb +59 -0
  46. data/lib/jwt/signature.rb +35 -0
  47. data/lib/jwt/verify.rb +113 -0
  48. data/lib/jwt/version.rb +28 -0
  49. data/lib/jwt/x5c_key_finder.rb +55 -0
  50. data/lib/jwt.rb +20 -215
  51. data/ruby-jwt.gemspec +35 -0
  52. metadata +138 -30
  53. data/Manifest +0 -6
  54. data/jwt.gemspec +0 -34
  55. data/spec/helper.rb +0 -2
  56. data/spec/jwt_spec.rb +0 -434
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Ecdsa
6
+ module_function
7
+
8
+ NAMED_CURVES = {
9
+ 'prime256v1' => {
10
+ algorithm: 'ES256',
11
+ digest: 'sha256'
12
+ },
13
+ 'secp256r1' => { # alias for prime256v1
14
+ algorithm: 'ES256',
15
+ digest: 'sha256'
16
+ },
17
+ 'secp384r1' => {
18
+ algorithm: 'ES384',
19
+ digest: 'sha384'
20
+ },
21
+ 'secp521r1' => {
22
+ algorithm: 'ES512',
23
+ digest: 'sha512'
24
+ },
25
+ 'secp256k1' => {
26
+ algorithm: 'ES256K',
27
+ digest: 'sha256'
28
+ }
29
+ }.freeze
30
+
31
+ SUPPORTED = NAMED_CURVES.map { |_, c| c[:algorithm] }.uniq.freeze
32
+
33
+ def sign(to_sign)
34
+ algorithm, msg, key = to_sign.values
35
+ curve_definition = curve_by_name(key.group.curve_name)
36
+ key_algorithm = curve_definition[:algorithm]
37
+ if algorithm != key_algorithm
38
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
39
+ end
40
+
41
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
42
+ SecurityUtils.asn1_to_raw(key.dsa_sign_asn1(digest.digest(msg)), key)
43
+ end
44
+
45
+ def verify(to_verify)
46
+ algorithm, public_key, signing_input, signature = to_verify.values
47
+ curve_definition = curve_by_name(public_key.group.curve_name)
48
+ key_algorithm = curve_definition[:algorithm]
49
+ if algorithm != key_algorithm
50
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
51
+ end
52
+
53
+ digest = OpenSSL::Digest.new(curve_definition[:digest])
54
+ public_key.dsa_verify_asn1(digest.digest(signing_input), SecurityUtils.raw_to_asn1(signature, public_key))
55
+ end
56
+
57
+ def curve_by_name(name)
58
+ NAMED_CURVES.fetch(name) do
59
+ raise UnsupportedEcdsaCurve, "The ECDSA curve '#{name}' is not supported"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Eddsa
6
+ module_function
7
+
8
+ SUPPORTED = %w[ED25519 EdDSA].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
12
+ if key.class != RbNaCl::Signatures::Ed25519::SigningKey
13
+ raise EncodeError, "Key given is a #{key.class} but has to be an RbNaCl::Signatures::Ed25519::SigningKey"
14
+ end
15
+ unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
16
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
17
+ end
18
+
19
+ key.sign(msg)
20
+ end
21
+
22
+ def verify(to_verify)
23
+ algorithm, public_key, signing_input, signature = to_verify.values
24
+ unless SUPPORTED.map(&:downcase).map(&:to_sym).include?(algorithm.downcase.to_sym)
25
+ raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key.primitive} signing key was provided"
26
+ end
27
+ raise DecodeError, "key given is a #{public_key.class} but has to be a RbNaCl::Signatures::Ed25519::VerifyKey" if public_key.class != RbNaCl::Signatures::Ed25519::VerifyKey
28
+
29
+ public_key.verify(signature, signing_input)
30
+ rescue RbNaCl::CryptoError
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Hmac
6
+ module_function
7
+
8
+ SUPPORTED = %w[HS256 HS512256 HS384 HS512].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
12
+ key ||= ''
13
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, key)
14
+ if authenticator && padded_key
15
+ authenticator.auth(padded_key, msg.encode('binary'))
16
+ else
17
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
18
+ end
19
+ end
20
+
21
+ def verify(to_verify)
22
+ algorithm, public_key, signing_input, signature = to_verify.values
23
+ authenticator, padded_key = SecurityUtils.rbnacl_fixup(algorithm, public_key)
24
+ if authenticator && padded_key
25
+ begin
26
+ authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
27
+ rescue RbNaCl::BadAuthenticatorError
28
+ false
29
+ end
30
+ else
31
+ SecurityUtils.secure_compare(signature, sign(JWT::Signature::ToSign.new(algorithm, signing_input, public_key)))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module None
6
+ module_function
7
+
8
+ SUPPORTED = %w[none].freeze
9
+
10
+ def sign(*); end
11
+
12
+ def verify(*)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Ps
6
+ # RSASSA-PSS signing algorithms
7
+
8
+ module_function
9
+
10
+ SUPPORTED = %w[PS256 PS384 PS512].freeze
11
+
12
+ def sign(to_sign)
13
+ require_openssl!
14
+
15
+ algorithm, msg, key = to_sign.values
16
+
17
+ key_class = key.class
18
+
19
+ raise EncodeError, "The given key is a #{key_class}. It has to be an OpenSSL::PKey::RSA instance." if key_class == String
20
+
21
+ translated_algorithm = algorithm.sub('PS', 'sha')
22
+
23
+ key.sign_pss(translated_algorithm, msg, salt_length: :digest, mgf1_hash: translated_algorithm)
24
+ end
25
+
26
+ def verify(to_verify)
27
+ require_openssl!
28
+
29
+ SecurityUtils.verify_ps(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
30
+ end
31
+
32
+ def require_openssl!
33
+ if Object.const_defined?('OpenSSL')
34
+ if ::Gem::Version.new(OpenSSL::VERSION) < ::Gem::Version.new('2.1')
35
+ raise JWT::RequiredDependencyError, "You currently have OpenSSL #{OpenSSL::VERSION}. PS support requires >= 2.1"
36
+ end
37
+ else
38
+ raise JWT::RequiredDependencyError, 'PS signing requires OpenSSL +2.1'
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Rsa
6
+ module_function
7
+
8
+ SUPPORTED = %w[RS256 RS384 RS512].freeze
9
+
10
+ def sign(to_sign)
11
+ algorithm, msg, key = to_sign.values
12
+ raise EncodeError, "The given key is a #{key.class}. It has to be an OpenSSL::PKey::RSA instance." if key.instance_of?(String)
13
+
14
+ key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
15
+ end
16
+
17
+ def verify(to_verify)
18
+ SecurityUtils.verify_rsa(to_verify.algorithm, to_verify.public_key, to_verify.signing_input, to_verify.signature)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Algos
5
+ module Unsupported
6
+ module_function
7
+
8
+ SUPPORTED = [].freeze
9
+
10
+ def sign(*)
11
+ raise NotImplementedError, 'Unsupported signing method'
12
+ end
13
+
14
+ def verify(*)
15
+ raise JWT::VerificationError, 'Algorithm not supported'
16
+ end
17
+ end
18
+ end
19
+ end
data/lib/jwt/algos.rb ADDED
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt/algos/hmac'
4
+ require 'jwt/algos/eddsa'
5
+ require 'jwt/algos/ecdsa'
6
+ require 'jwt/algos/rsa'
7
+ require 'jwt/algos/ps'
8
+ require 'jwt/algos/none'
9
+ require 'jwt/algos/unsupported'
10
+
11
+ # JWT::Signature module
12
+ module JWT
13
+ # Signature logic for JWT
14
+ module Algos
15
+ extend self
16
+
17
+ ALGOS = [
18
+ Algos::Hmac,
19
+ Algos::Ecdsa,
20
+ Algos::Rsa,
21
+ Algos::Eddsa,
22
+ Algos::Ps,
23
+ Algos::None,
24
+ Algos::Unsupported
25
+ ].freeze
26
+
27
+ def find(algorithm)
28
+ indexed[algorithm && algorithm.downcase]
29
+ end
30
+
31
+ private
32
+
33
+ def indexed
34
+ @indexed ||= begin
35
+ fallback = [Algos::Unsupported, nil]
36
+ ALGOS.each_with_object(Hash.new(fallback)) do |alg, hash|
37
+ alg.const_get(:SUPPORTED).each do |code|
38
+ hash[code.downcase] = [alg, code]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
data/lib/jwt/base64.rb ADDED
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './error'
4
+
5
+ module JWT
6
+ class ClaimsValidator
7
+ NUMERIC_CLAIMS = %i[
8
+ exp
9
+ iat
10
+ nbf
11
+ ].freeze
12
+
13
+ def initialize(payload)
14
+ @payload = payload.transform_keys(&:to_sym)
15
+ end
16
+
17
+ def validate!
18
+ validate_numeric_claims
19
+
20
+ true
21
+ end
22
+
23
+ private
24
+
25
+ def validate_numeric_claims
26
+ NUMERIC_CLAIMS.each do |claim|
27
+ validate_is_numeric(claim) if @payload.key?(claim)
28
+ end
29
+ end
30
+
31
+ def validate_is_numeric(claim)
32
+ return if @payload[claim].is_a?(Numeric)
33
+
34
+ raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'decode_configuration'
4
+ require_relative 'jwk_configuration'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class Container
9
+ attr_accessor :decode, :jwk
10
+
11
+ def initialize
12
+ reset!
13
+ end
14
+
15
+ def reset!
16
+ @decode = DecodeConfiguration.new
17
+ @jwk = JwkConfiguration.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Configuration
5
+ class DecodeConfiguration
6
+ attr_accessor :verify_expiration,
7
+ :verify_not_before,
8
+ :verify_iss,
9
+ :verify_iat,
10
+ :verify_jti,
11
+ :verify_aud,
12
+ :verify_sub,
13
+ :leeway,
14
+ :algorithms,
15
+ :required_claims
16
+
17
+ def initialize
18
+ @verify_expiration = true
19
+ @verify_not_before = true
20
+ @verify_iss = false
21
+ @verify_iat = false
22
+ @verify_jti = false
23
+ @verify_aud = false
24
+ @verify_sub = false
25
+ @leeway = 0
26
+ @algorithms = ['HS256']
27
+ @required_claims = []
28
+ end
29
+
30
+ def to_h
31
+ {
32
+ verify_expiration: verify_expiration,
33
+ verify_not_before: verify_not_before,
34
+ verify_iss: verify_iss,
35
+ verify_iat: verify_iat,
36
+ verify_jti: verify_jti,
37
+ verify_aud: verify_aud,
38
+ verify_sub: verify_sub,
39
+ leeway: leeway,
40
+ algorithms: algorithms,
41
+ required_claims: required_claims
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../jwk/kid_as_key_digest'
4
+ require_relative '../jwk/thumbprint'
5
+
6
+ module JWT
7
+ module Configuration
8
+ class JwkConfiguration
9
+ def initialize
10
+ self.kid_generator_type = :key_digest
11
+ end
12
+
13
+ def kid_generator_type=(value)
14
+ self.kid_generator = case value
15
+ when :key_digest
16
+ JWT::JWK::KidAsKeyDigest
17
+ when :rfc7638_thumbprint
18
+ JWT::JWK::Thumbprint
19
+ else
20
+ raise ArgumentError, "#{value} is not a valid kid generator type."
21
+ end
22
+ end
23
+
24
+ attr_accessor :kid_generator
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/container'
4
+
5
+ module JWT
6
+ module Configuration
7
+ def configure
8
+ yield(configuration)
9
+ end
10
+
11
+ def configuration
12
+ @configuration ||= ::JWT::Configuration::Container.new
13
+ end
14
+ end
15
+ end
data/lib/jwt/decode.rb ADDED
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require 'jwt/signature'
6
+ require 'jwt/verify'
7
+ require 'jwt/x5c_key_finder'
8
+ # JWT::Decode module
9
+ module JWT
10
+ # Decoding logic for JWT
11
+ class Decode
12
+ def initialize(jwt, key, verify, options, &keyfinder)
13
+ raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
14
+
15
+ @jwt = jwt
16
+ @key = key
17
+ @options = options
18
+ @segments = jwt.split('.')
19
+ @verify = verify
20
+ @signature = ''
21
+ @keyfinder = keyfinder
22
+ end
23
+
24
+ def decode_segments
25
+ validate_segment_count!
26
+ if @verify
27
+ decode_crypto
28
+ verify_algo
29
+ set_key
30
+ verify_signature
31
+ verify_claims
32
+ end
33
+ raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
34
+
35
+ [payload, header]
36
+ end
37
+
38
+ private
39
+
40
+ def verify_signature
41
+ return unless @key || @verify
42
+
43
+ return if none_algorithm?
44
+
45
+ raise JWT::DecodeError, 'No verification key available' unless @key
46
+
47
+ return if Array(@key).any? { |key| verify_signature_for?(key) }
48
+
49
+ raise(JWT::VerificationError, 'Signature verification failed')
50
+ end
51
+
52
+ def verify_algo
53
+ raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
54
+ raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless algorithm
55
+ raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header?
56
+ end
57
+
58
+ def set_key
59
+ @key = find_key(&@keyfinder) if @keyfinder
60
+ @key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
61
+ if (x5c_options = @options[:x5c])
62
+ @key = X5cKeyFinder.new(x5c_options[:root_certificates], x5c_options[:crls]).from(header['x5c'])
63
+ end
64
+ end
65
+
66
+ def verify_signature_for?(key)
67
+ Signature.verify(algorithm, key, signing_input, @signature)
68
+ end
69
+
70
+ def options_includes_algo_in_header?
71
+ allowed_algorithms.any? { |alg| alg.casecmp(algorithm).zero? }
72
+ end
73
+
74
+ def allowed_algorithms
75
+ # Order is very important - first check for string keys, next for symbols
76
+ algos = if @options.key?('algorithm')
77
+ @options['algorithm']
78
+ elsif @options.key?(:algorithm)
79
+ @options[:algorithm]
80
+ elsif @options.key?('algorithms')
81
+ @options['algorithms']
82
+ elsif @options.key?(:algorithms)
83
+ @options[:algorithms]
84
+ else
85
+ []
86
+ end
87
+ Array(algos)
88
+ end
89
+
90
+ def find_key(&keyfinder)
91
+ key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
92
+ # key can be of type [string, nil, OpenSSL::PKey, Array]
93
+ return key if key && !Array(key).empty?
94
+
95
+ raise JWT::DecodeError, 'No verification key available'
96
+ end
97
+
98
+ def verify_claims
99
+ Verify.verify_claims(payload, @options)
100
+ Verify.verify_required_claims(payload, @options)
101
+ end
102
+
103
+ def validate_segment_count!
104
+ return if segment_length == 3
105
+ return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
106
+ return if segment_length == 2 && none_algorithm?
107
+
108
+ raise(JWT::DecodeError, 'Not enough or too many segments')
109
+ end
110
+
111
+ def segment_length
112
+ @segments.count
113
+ end
114
+
115
+ def none_algorithm?
116
+ algorithm == 'none'
117
+ end
118
+
119
+ def decode_crypto
120
+ @signature = ::JWT::Base64.url_decode(@segments[2] || '')
121
+ end
122
+
123
+ def algorithm
124
+ header['alg']
125
+ end
126
+
127
+ def header
128
+ @header ||= parse_and_decode @segments[0]
129
+ end
130
+
131
+ def payload
132
+ @payload ||= parse_and_decode @segments[1]
133
+ end
134
+
135
+ def signing_input
136
+ @segments.first(2).join('.')
137
+ end
138
+
139
+ def parse_and_decode(segment)
140
+ JWT::JSON.parse(::JWT::Base64.url_decode(segment))
141
+ rescue ::JSON::ParserError
142
+ raise JWT::DecodeError, 'Invalid segment encoding'
143
+ end
144
+ end
145
+ end
data/lib/jwt/encode.rb ADDED
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './algos'
4
+ require_relative './claims_validator'
5
+
6
+ # JWT::Encode module
7
+ module JWT
8
+ # Encoding logic for JWT
9
+ class Encode
10
+ ALG_NONE = 'none'
11
+ ALG_KEY = 'alg'
12
+
13
+ def initialize(options)
14
+ @payload = options[:payload]
15
+ @key = options[:key]
16
+ _, @algorithm = Algos.find(options[:algorithm])
17
+ @headers = options[:headers].transform_keys(&:to_s)
18
+ end
19
+
20
+ def segments
21
+ @segments ||= combine(encoded_header_and_payload, encoded_signature)
22
+ end
23
+
24
+ private
25
+
26
+ def encoded_header
27
+ @encoded_header ||= encode_header
28
+ end
29
+
30
+ def encoded_payload
31
+ @encoded_payload ||= encode_payload
32
+ end
33
+
34
+ def encoded_signature
35
+ @encoded_signature ||= encode_signature
36
+ end
37
+
38
+ def encoded_header_and_payload
39
+ @encoded_header_and_payload ||= combine(encoded_header, encoded_payload)
40
+ end
41
+
42
+ def encode_header
43
+ @headers[ALG_KEY] = @algorithm
44
+ encode(@headers)
45
+ end
46
+
47
+ def encode_payload
48
+ if @payload.is_a?(Hash)
49
+ ClaimsValidator.new(@payload).validate!
50
+ end
51
+
52
+ encode(@payload)
53
+ end
54
+
55
+ def encode_signature
56
+ return '' if @algorithm == ALG_NONE
57
+
58
+ ::JWT::Base64.url_encode(JWT::Signature.sign(@algorithm, encoded_header_and_payload, @key))
59
+ end
60
+
61
+ def encode(data)
62
+ ::JWT::Base64.url_encode(JWT::JSON.generate(data))
63
+ end
64
+
65
+ def combine(*parts)
66
+ parts.join('.')
67
+ end
68
+ end
69
+ end
data/lib/jwt/error.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ class EncodeError < StandardError; end
5
+ class DecodeError < StandardError; end
6
+ class RequiredDependencyError < StandardError; end
7
+
8
+ class VerificationError < DecodeError; end
9
+ class ExpiredSignature < DecodeError; end
10
+ class IncorrectAlgorithm < DecodeError; end
11
+ class ImmatureSignature < DecodeError; end
12
+ class InvalidIssuerError < DecodeError; end
13
+ class UnsupportedEcdsaCurve < IncorrectAlgorithm; end
14
+ class InvalidIatError < DecodeError; end
15
+ class InvalidAudError < DecodeError; end
16
+ class InvalidSubError < DecodeError; end
17
+ class InvalidJtiError < DecodeError; end
18
+ class InvalidPayload < DecodeError; end
19
+ class MissingRequiredClaim < DecodeError; end
20
+
21
+ class JWKError < DecodeError; end
22
+ end