jwt-blockstack 2.0.0.beta1
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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +411 -0
- data/Gemfile +4 -0
- data/LICENSE +7 -0
- data/Manifest +8 -0
- data/README.md +443 -0
- data/Rakefile +11 -0
- data/lib/jwt.rb +67 -0
- data/lib/jwt/decode.rb +45 -0
- data/lib/jwt/default_options.rb +14 -0
- data/lib/jwt/encode.rb +51 -0
- data/lib/jwt/error.rb +15 -0
- data/lib/jwt/signature.rb +146 -0
- data/lib/jwt/verify.rb +84 -0
- data/lib/jwt/version.rb +24 -0
- data/ruby-jwt-blockstack.gemspec +31 -0
- data/spec/fixtures/certs/ec256-private.pem +8 -0
- data/spec/fixtures/certs/ec256-public.pem +4 -0
- data/spec/fixtures/certs/ec256-wrong-private.pem +8 -0
- data/spec/fixtures/certs/ec256-wrong-public.pem +4 -0
- data/spec/fixtures/certs/ec384-private.pem +9 -0
- data/spec/fixtures/certs/ec384-public.pem +5 -0
- data/spec/fixtures/certs/ec384-wrong-private.pem +9 -0
- data/spec/fixtures/certs/ec384-wrong-public.pem +5 -0
- data/spec/fixtures/certs/ec512-private.pem +10 -0
- data/spec/fixtures/certs/ec512-public.pem +6 -0
- data/spec/fixtures/certs/ec512-wrong-private.pem +10 -0
- data/spec/fixtures/certs/ec512-wrong-public.pem +6 -0
- data/spec/fixtures/certs/rsa-1024-private.pem +15 -0
- data/spec/fixtures/certs/rsa-1024-public.pem +6 -0
- data/spec/fixtures/certs/rsa-2048-private.pem +27 -0
- data/spec/fixtures/certs/rsa-2048-public.pem +9 -0
- data/spec/fixtures/certs/rsa-2048-wrong-private.pem +27 -0
- data/spec/fixtures/certs/rsa-2048-wrong-public.pem +9 -0
- data/spec/fixtures/certs/rsa-4096-private.pem +51 -0
- data/spec/fixtures/certs/rsa-4096-public.pem +14 -0
- data/spec/integration/readme_examples_spec.rb +216 -0
- data/spec/jwt/verify_spec.rb +190 -0
- data/spec/jwt_spec.rb +233 -0
- data/spec/spec_helper.rb +28 -0
- metadata +225 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'bundler/gem_tasks'
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
|
|
6
|
+
RSpec::Core::RakeTask.new(:test)
|
|
7
|
+
|
|
8
|
+
task default: :test
|
|
9
|
+
rescue LoadError
|
|
10
|
+
puts 'RSpec rake tasks not available. Please run "bundle install" to install missing dependencies.'
|
|
11
|
+
end
|
data/lib/jwt.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'jwt/decode'
|
|
4
|
+
require 'jwt/default_options'
|
|
5
|
+
require 'jwt/encode'
|
|
6
|
+
require 'jwt/error'
|
|
7
|
+
require 'jwt/signature'
|
|
8
|
+
require 'jwt/verify'
|
|
9
|
+
|
|
10
|
+
# JSON Web Token implementation
|
|
11
|
+
#
|
|
12
|
+
# Should be up to date with the latest spec:
|
|
13
|
+
# https://tools.ietf.org/html/rfc7519
|
|
14
|
+
module JWT
|
|
15
|
+
include JWT::DefaultOptions
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
def decoded_segments(jwt, verify = true)
|
|
20
|
+
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
|
|
21
|
+
|
|
22
|
+
decoder = Decode.new jwt, verify
|
|
23
|
+
decoder.decode_segments
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def encode(payload, key, algorithm = 'HS256', header_fields = {})
|
|
27
|
+
encoder = Encode.new payload, key, algorithm, header_fields
|
|
28
|
+
encoder.segments
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
|
|
32
|
+
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
|
|
33
|
+
|
|
34
|
+
merged_options = DEFAULT_OPTIONS.merge(custom_options)
|
|
35
|
+
|
|
36
|
+
decoder = Decode.new jwt, verify
|
|
37
|
+
header, payload, signature, signing_input = decoder.decode_segments
|
|
38
|
+
decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
|
|
39
|
+
|
|
40
|
+
Verify.verify_claims(payload, merged_options)
|
|
41
|
+
|
|
42
|
+
raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
|
|
43
|
+
|
|
44
|
+
[payload, header]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
|
|
48
|
+
algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
|
|
49
|
+
|
|
50
|
+
raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') unless options[:algorithm]
|
|
51
|
+
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo == options[:algorithm]
|
|
52
|
+
|
|
53
|
+
Signature.verify(algo, key, signing_input, signature)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def signature_algorithm_and_key(header, payload, key, &keyfinder)
|
|
57
|
+
if keyfinder
|
|
58
|
+
key = if keyfinder.arity == 2
|
|
59
|
+
yield(header, payload)
|
|
60
|
+
else
|
|
61
|
+
yield(header)
|
|
62
|
+
end
|
|
63
|
+
raise JWT::DecodeError, 'No verification key available' unless key
|
|
64
|
+
end
|
|
65
|
+
[header['alg'], key]
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/jwt/decode.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
# JWT::Decode module
|
|
5
|
+
module JWT
|
|
6
|
+
# Decoding logic for JWT
|
|
7
|
+
class Decode
|
|
8
|
+
attr_reader :header, :payload, :signature
|
|
9
|
+
|
|
10
|
+
def self.base64url_decode(str)
|
|
11
|
+
str += '=' * (4 - str.length.modulo(4))
|
|
12
|
+
Base64.decode64(str.tr('-_', '+/'))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(jwt, verify)
|
|
16
|
+
@jwt = jwt
|
|
17
|
+
@verify = verify
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def decode_segments
|
|
21
|
+
header_segment, payload_segment, crypto_segment = raw_segments(@jwt, @verify)
|
|
22
|
+
@header, @payload = decode_header_and_payload(header_segment, payload_segment)
|
|
23
|
+
@signature = Decode.base64url_decode(crypto_segment.to_s) if @verify
|
|
24
|
+
signing_input = [header_segment, payload_segment].join('.')
|
|
25
|
+
[@header, @payload, @signature, signing_input]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def raw_segments(jwt, verify)
|
|
31
|
+
segments = jwt.split('.')
|
|
32
|
+
required_num_segments = verify ? [3] : [2, 3]
|
|
33
|
+
raise(JWT::DecodeError, 'Not enough or too many segments') unless required_num_segments.include? segments.length
|
|
34
|
+
segments
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def decode_header_and_payload(header_segment, payload_segment)
|
|
38
|
+
header = JSON.parse(Decode.base64url_decode(header_segment))
|
|
39
|
+
payload = JSON.parse(Decode.base64url_decode(payload_segment))
|
|
40
|
+
[header, payload]
|
|
41
|
+
rescue JSON::ParserError
|
|
42
|
+
raise JWT::DecodeError, 'Invalid segment encoding'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module JWT
|
|
2
|
+
module DefaultOptions
|
|
3
|
+
DEFAULT_OPTIONS = {
|
|
4
|
+
verify_expiration: true,
|
|
5
|
+
verify_not_before: true,
|
|
6
|
+
verify_iss: false,
|
|
7
|
+
verify_iat: false,
|
|
8
|
+
verify_jti: false,
|
|
9
|
+
verify_aud: false,
|
|
10
|
+
verify_sub: false,
|
|
11
|
+
leeway: 0
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/jwt/encode.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
# JWT::Encode module
|
|
5
|
+
module JWT
|
|
6
|
+
# Encoding logic for JWT
|
|
7
|
+
class Encode
|
|
8
|
+
attr_reader :payload, :key, :algorithm, :header_fields, :segments
|
|
9
|
+
|
|
10
|
+
def self.base64url_encode(str)
|
|
11
|
+
Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(payload, key, algorithm, header_fields)
|
|
15
|
+
@payload = payload
|
|
16
|
+
@key = key
|
|
17
|
+
@algorithm = algorithm
|
|
18
|
+
@header_fields = header_fields
|
|
19
|
+
@segments = encode_segments
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def encoded_header(algorithm, header_fields)
|
|
25
|
+
header = { 'alg' => algorithm }.merge(header_fields)
|
|
26
|
+
Encode.base64url_encode(JSON.generate(header))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def encoded_payload(payload)
|
|
30
|
+
raise InvalidPayload, 'exp claim must be an integer' if payload['exp'] && payload['exp'].is_a?(Time)
|
|
31
|
+
Encode.base64url_encode(JSON.generate(payload))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def encoded_signature(signing_input, key, algorithm)
|
|
35
|
+
if algorithm == 'none'
|
|
36
|
+
''
|
|
37
|
+
else
|
|
38
|
+
signature = JWT::Signature.sign(algorithm, signing_input, key)
|
|
39
|
+
Encode.base64url_encode(signature)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def encode_segments
|
|
44
|
+
segments = []
|
|
45
|
+
segments << encoded_header(@algorithm, @header_fields)
|
|
46
|
+
segments << encoded_payload(@payload)
|
|
47
|
+
segments << encoded_signature(segments.join('.'), @key, @algorithm)
|
|
48
|
+
segments.join('.')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/jwt/error.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module JWT
|
|
3
|
+
class EncodeError < StandardError; end
|
|
4
|
+
class DecodeError < StandardError; end
|
|
5
|
+
class VerificationError < DecodeError; end
|
|
6
|
+
class ExpiredSignature < DecodeError; end
|
|
7
|
+
class IncorrectAlgorithm < DecodeError; end
|
|
8
|
+
class ImmatureSignature < DecodeError; end
|
|
9
|
+
class InvalidIssuerError < DecodeError; end
|
|
10
|
+
class InvalidIatError < DecodeError; end
|
|
11
|
+
class InvalidAudError < DecodeError; end
|
|
12
|
+
class InvalidSubError < DecodeError; end
|
|
13
|
+
class InvalidJtiError < DecodeError; end
|
|
14
|
+
class InvalidPayload < DecodeError; end
|
|
15
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'openssl'
|
|
3
|
+
begin
|
|
4
|
+
require 'rbnacl'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# JWT::Signature module
|
|
9
|
+
module JWT
|
|
10
|
+
# Signature logic for JWT
|
|
11
|
+
module Signature
|
|
12
|
+
extend self
|
|
13
|
+
|
|
14
|
+
HMAC_ALGORITHMS = %w(HS256 HS512256 HS384 HS512).freeze
|
|
15
|
+
RSA_ALGORITHMS = %w(RS256 RS384 RS512).freeze
|
|
16
|
+
ECDSA_ALGORITHMS = %w(ES256 ES384 ES512 ES256K).freeze
|
|
17
|
+
|
|
18
|
+
NAMED_CURVES = {
|
|
19
|
+
'prime256v1' => 'ES256',
|
|
20
|
+
'secp384r1' => 'ES384',
|
|
21
|
+
'secp521r1' => 'ES512',
|
|
22
|
+
'secp256k1' => 'ES256K'
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
def sign(algorithm, msg, key)
|
|
26
|
+
if HMAC_ALGORITHMS.include?(algorithm)
|
|
27
|
+
sign_hmac(algorithm, msg, key)
|
|
28
|
+
elsif RSA_ALGORITHMS.include?(algorithm)
|
|
29
|
+
sign_rsa(algorithm, msg, key)
|
|
30
|
+
elsif ECDSA_ALGORITHMS.include?(algorithm)
|
|
31
|
+
sign_ecdsa(algorithm, msg, key)
|
|
32
|
+
else
|
|
33
|
+
raise NotImplementedError, 'Unsupported signing method'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def verify(algo, key, signing_input, signature)
|
|
38
|
+
verified = if HMAC_ALGORITHMS.include?(algo)
|
|
39
|
+
verify_hmac(algo, key, signing_input, signature)
|
|
40
|
+
elsif RSA_ALGORITHMS.include?(algo)
|
|
41
|
+
verify_rsa(algo, key, signing_input, signature)
|
|
42
|
+
elsif ECDSA_ALGORITHMS.include?(algo)
|
|
43
|
+
verify_ecdsa(algo, key, signing_input, signature)
|
|
44
|
+
else
|
|
45
|
+
raise JWT::VerificationError, 'Algorithm not supported'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
raise(JWT::VerificationError, 'Signature verification raised') unless verified
|
|
49
|
+
rescue OpenSSL::PKey::PKeyError
|
|
50
|
+
raise JWT::VerificationError, 'Signature verification raised'
|
|
51
|
+
ensure
|
|
52
|
+
OpenSSL.errors.clear
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def sign_rsa(algorithm, msg, private_key)
|
|
58
|
+
raise EncodeError, "The given key is a #{private_key.class}. It has to be an OpenSSL::PKey::RSA instance." if private_key.class == String
|
|
59
|
+
private_key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def sign_ecdsa(algorithm, msg, private_key)
|
|
63
|
+
key_algorithm = NAMED_CURVES[private_key.group.curve_name]
|
|
64
|
+
if algorithm != key_algorithm
|
|
65
|
+
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha').sub('K', ''))
|
|
69
|
+
asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def sign_hmac(algorithm, msg, key)
|
|
73
|
+
authenticator, padded_key = rbnacl_fixup(algorithm, key)
|
|
74
|
+
if authenticator && padded_key
|
|
75
|
+
authenticator.auth(padded_key, msg.encode('binary'))
|
|
76
|
+
else
|
|
77
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def verify_rsa(algorithm, public_key, signing_input, signature)
|
|
82
|
+
public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def verify_ecdsa(algorithm, public_key, signing_input, signature)
|
|
86
|
+
key_algorithm = NAMED_CURVES[public_key.group.curve_name]
|
|
87
|
+
if algorithm != key_algorithm
|
|
88
|
+
raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha').sub('K', ''))
|
|
92
|
+
public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def verify_hmac(algorithm, public_key, signing_input, signature)
|
|
96
|
+
authenticator, padded_key = rbnacl_fixup(algorithm, public_key)
|
|
97
|
+
if authenticator && padded_key
|
|
98
|
+
begin
|
|
99
|
+
authenticator.verify(padded_key, signature.encode('binary'), signing_input.encode('binary'))
|
|
100
|
+
rescue RbNaCl::BadAuthenticatorError
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
secure_compare(signature, sign_hmac(algorithm, signing_input, public_key))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def asn1_to_raw(signature, public_key)
|
|
109
|
+
byte_size = (public_key.group.degree + 7) / 8
|
|
110
|
+
OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def raw_to_asn1(signature, private_key)
|
|
114
|
+
byte_size = (private_key.group.degree + 7) / 8
|
|
115
|
+
r = signature[0..(byte_size - 1)]
|
|
116
|
+
s = signature[byte_size..-1] || ''
|
|
117
|
+
OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def rbnacl_fixup(algorithm, key)
|
|
121
|
+
algorithm = algorithm.sub('HS', 'SHA').to_sym
|
|
122
|
+
|
|
123
|
+
return [] unless defined?(RbNaCl) && RbNaCl::HMAC.constants(false).include?(algorithm)
|
|
124
|
+
|
|
125
|
+
authenticator = RbNaCl::HMAC.const_get(algorithm)
|
|
126
|
+
|
|
127
|
+
# Fall back to OpenSSL for keys larger than 32 bytes.
|
|
128
|
+
return [] if key.bytesize > authenticator.key_bytes
|
|
129
|
+
|
|
130
|
+
[
|
|
131
|
+
authenticator,
|
|
132
|
+
key.bytes.fill(0, key.bytesize...authenticator.key_bytes).pack('C*')
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# From devise
|
|
137
|
+
# constant-time comparison algorithm to prevent timing attacks
|
|
138
|
+
def secure_compare(a, b)
|
|
139
|
+
return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
|
|
140
|
+
l = a.unpack "C#{a.bytesize}"
|
|
141
|
+
res = 0
|
|
142
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
|
143
|
+
res.zero?
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
data/lib/jwt/verify.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'jwt/error'
|
|
3
|
+
|
|
4
|
+
module JWT
|
|
5
|
+
# JWT verify methods
|
|
6
|
+
class Verify
|
|
7
|
+
class << self
|
|
8
|
+
%w(verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub).each do |method_name|
|
|
9
|
+
define_method method_name do |payload, options|
|
|
10
|
+
new(payload, options).send(method_name)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def verify_claims(payload, options)
|
|
15
|
+
options.each do |key, val|
|
|
16
|
+
next unless key.to_s =~ /verify/
|
|
17
|
+
Verify.send(key, payload, options) if val
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(payload, options)
|
|
23
|
+
@payload = payload
|
|
24
|
+
@options = options
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def verify_aud
|
|
28
|
+
return unless (options_aud = @options[:aud])
|
|
29
|
+
raise(JWT::InvalidAudError, "Invalid audience. Expected #{options_aud}, received #{@payload['aud'] || '<none>'}") if ([*@payload['aud']] & [*options_aud]).empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def verify_expiration
|
|
33
|
+
return unless @payload.include?('exp')
|
|
34
|
+
raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def verify_iat
|
|
38
|
+
return unless @payload.include?('iat')
|
|
39
|
+
raise(JWT::InvalidIatError, 'Invalid iat') if !@payload['iat'].is_a?(Numeric) || @payload['iat'].to_f > (Time.now.to_f + iat_leeway)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def verify_iss
|
|
43
|
+
return unless (options_iss = @options[:iss])
|
|
44
|
+
raise(JWT::InvalidIssuerError, "Invalid issuer. Expected #{options_iss}, received #{@payload['iss'] || '<none>'}") if @payload['iss'].to_s != options_iss.to_s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def verify_jti
|
|
48
|
+
options_verify_jti = @options[:verify_jti]
|
|
49
|
+
if options_verify_jti.respond_to?(:call)
|
|
50
|
+
raise(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(@payload['jti'])
|
|
51
|
+
elsif @payload['jti'].to_s.strip.empty?
|
|
52
|
+
raise(JWT::InvalidJtiError, 'Missing jti')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def verify_not_before
|
|
57
|
+
return unless @payload.include?('nbf')
|
|
58
|
+
raise(JWT::ImmatureSignature, 'Signature nbf has not been reached') if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def verify_sub
|
|
62
|
+
return unless (options_sub = @options[:sub])
|
|
63
|
+
raise(JWT::InvalidSubError, "Invalid subject. Expected #{options_sub}, received #{@payload['sub'] || '<none>'}") unless @payload['sub'].to_s == options_sub.to_s
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def global_leeway
|
|
69
|
+
@options[:leeway]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def exp_leeway
|
|
73
|
+
@options[:exp_leeway] || global_leeway
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def iat_leeway
|
|
77
|
+
@options[:iat_leeway] || global_leeway
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def nbf_leeway
|
|
81
|
+
@options[:nbf_leeway] || global_leeway
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/jwt/version.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Moments version builder module
|
|
5
|
+
module JWT
|
|
6
|
+
def self.gem_version
|
|
7
|
+
Gem::Version.new VERSION::STRING
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Moments version builder module
|
|
11
|
+
module VERSION
|
|
12
|
+
# major version
|
|
13
|
+
MAJOR = 2
|
|
14
|
+
# minor version
|
|
15
|
+
MINOR = 0
|
|
16
|
+
# tiny version
|
|
17
|
+
TINY = 0
|
|
18
|
+
# alpha, beta, etc. tag
|
|
19
|
+
PRE = 'beta1'.freeze
|
|
20
|
+
|
|
21
|
+
# Build version string
|
|
22
|
+
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
|
23
|
+
end
|
|
24
|
+
end
|