jwt 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee5f493cb3c4ed9c97a60b59f7377f410b280218
4
- data.tar.gz: f86ae6f76dbdd064ff4a7a83370b1ba343981d90
3
+ metadata.gz: 20bef3dbabb4260b05cd6119dc1b552d630bd3e1
4
+ data.tar.gz: bb02d4a2bb19011a8eb531020d4780f31d44af7b
5
5
  SHA512:
6
- metadata.gz: 79802a75028b87314162658551582a1c4ace40439ac718bc8ac82ed57ccaa9b5d0ac695cf4daa318bcc89616ddc8e93bf3ff79d7cb4c47903c03aee51e5bb8a1
7
- data.tar.gz: c14849f306b4952c7eba478d8fbc11c0a5045f38b931b563c67fef6367d9a8e89a66b78cccd9c8e4fbc546645e221b9b70437cde4aed18ca584269a95e94e9b6
6
+ metadata.gz: b8a0290f1b20390efb50c613da54a37072ebceee5dc11a9cec82cf67fa9e4002f3974bb35bd503b4dd7fdaacaed47c46f6f611aa8a66200cf44fbe40eaae8a2c
7
+ data.tar.gz: c9bb1d911b9d3cbabe362d69c6a556c8596f1dcd2485635875d02a74e93768b56fae8c5ee683ba03c5223b8696acda8080bbe408ea580ace5052ff48dfb7cb7b
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
+ # encoding: utf-8
1
2
  require 'rubygems'
2
3
  require 'rake'
3
4
  require 'echoe'
4
5
 
5
- Echoe.new('jwt', '1.5.0') do |p|
6
+ Echoe.new('jwt', '1.5.1') do |p|
6
7
  p.description = 'JSON Web Token implementation in Ruby'
7
8
  p.url = 'http://github.com/progrium/ruby-jwt'
8
9
  p.author = 'Jeff Lindsay'
data/jwt.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: jwt 1.5.0 ruby lib
2
+ # stub: jwt 1.5.1 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "jwt"
6
- s.version = "1.5.0"
6
+ s.version = "1.5.1"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib"]
10
10
  s.authors = ["Jeff Lindsay"]
11
- s.date = "2015-05-09"
11
+ s.date = "2015-06-22"
12
12
  s.description = "JSON Web Token implementation in Ruby"
13
13
  s.email = "progrium@gmail.com"
14
14
  s.extra_rdoc_files = ["lib/jwt.rb", "lib/jwt/json.rb"]
data/lib/jwt.rb CHANGED
@@ -1,13 +1,13 @@
1
- #
2
- # JSON Web Token implementation
3
- #
4
- # Should be up to date with the latest spec:
5
- # http://self-issued.info/docs/draft-jones-json-web-token-06.html
1
+ # encoding: utf-8
6
2
 
7
3
  require 'base64'
8
4
  require 'openssl'
9
5
  require 'jwt/json'
10
6
 
7
+ # JSON Web Token implementation
8
+ #
9
+ # Should be up to date with the latest spec:
10
+ # http://self-issued.info/docs/draft-jones-json-web-token-06.html
11
11
  module JWT
12
12
  class DecodeError < StandardError; end
13
13
  class VerificationError < DecodeError; end
@@ -24,7 +24,7 @@ module JWT
24
24
  NAMED_CURVES = {
25
25
  'prime256v1' => 'ES256',
26
26
  'secp384r1' => 'ES384',
27
- 'secp521r1' => 'ES512',
27
+ 'secp521r1' => 'ES512'
28
28
  }
29
29
 
30
30
  module_function
@@ -37,7 +37,7 @@ module JWT
37
37
  elsif ['ES256', 'ES384', 'ES512'].include?(algorithm)
38
38
  sign_ecdsa(algorithm, msg, key)
39
39
  else
40
- raise NotImplementedError.new('Unsupported signing method')
40
+ fail NotImplementedError.new('Unsupported signing method')
41
41
  end
42
42
  end
43
43
 
@@ -48,11 +48,11 @@ module JWT
48
48
  def sign_ecdsa(algorithm, msg, private_key)
49
49
  key_algorithm = NAMED_CURVES[private_key.group.curve_name]
50
50
  if algorithm != key_algorithm
51
- raise IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided")
51
+ fail IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided")
52
52
  end
53
53
 
54
54
  digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
55
- private_key.dsa_sign_asn1(digest.digest(msg))
55
+ asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
56
56
  end
57
57
 
58
58
  def verify_rsa(algorithm, public_key, signing_input, signature)
@@ -62,11 +62,11 @@ module JWT
62
62
  def verify_ecdsa(algorithm, public_key, signing_input, signature)
63
63
  key_algorithm = NAMED_CURVES[public_key.group.curve_name]
64
64
  if algorithm != key_algorithm
65
- raise IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided")
65
+ fail IncorrectAlgorithm.new("payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided")
66
66
  end
67
67
 
68
68
  digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
69
- public_key.dsa_verify_asn1(digest.digest(signing_input), signature)
69
+ public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
70
70
  end
71
71
 
72
72
  def sign_hmac(algorithm, msg, key)
@@ -83,7 +83,7 @@ module JWT
83
83
  end
84
84
 
85
85
  def encoded_header(algorithm='HS256', header_fields={})
86
- header = {'typ' => 'JWT', 'alg' => algorithm}.merge(header_fields)
86
+ header = { 'typ' => 'JWT', 'alg' => algorithm }.merge(header_fields)
87
87
  base64url_encode(encode_json(header))
88
88
  end
89
89
 
@@ -111,8 +111,8 @@ module JWT
111
111
 
112
112
  def raw_segments(jwt, verify=true)
113
113
  segments = jwt.split('.')
114
- required_num_segments = verify ? [3] : [2,3]
115
- raise JWT::DecodeError.new('Not enough or too many segments') unless required_num_segments.include? segments.length
114
+ required_num_segments = verify ? [3] : [2, 3]
115
+ fail JWT::DecodeError.new('Not enough or too many segments') unless required_num_segments.include? segments.length
116
116
  segments
117
117
  end
118
118
 
@@ -131,10 +131,10 @@ module JWT
131
131
  end
132
132
 
133
133
  def decode(jwt, key=nil, verify=true, options={}, &keyfinder)
134
- raise JWT::DecodeError.new('Nil JSON web token') unless jwt
134
+ fail JWT::DecodeError.new('Nil JSON web token') unless jwt
135
135
 
136
136
  header, payload, signature, signing_input = decoded_segments(jwt, verify)
137
- raise JWT::DecodeError.new('Not enough or too many segments') unless header && payload
137
+ fail JWT::DecodeError.new('Not enough or too many segments') unless header && payload
138
138
 
139
139
  default_options = {
140
140
  :verify_expiration => true,
@@ -152,64 +152,60 @@ module JWT
152
152
  if verify
153
153
  algo, key = signature_algorithm_and_key(header, key, &keyfinder)
154
154
  if options[:algorithm] && algo != options[:algorithm]
155
- raise JWT::IncorrectAlgorithm.new('Expected a different algorithm')
155
+ fail JWT::IncorrectAlgorithm.new('Expected a different algorithm')
156
156
  end
157
157
  verify_signature(algo, key, signing_input, signature)
158
158
  end
159
159
 
160
160
  if options[:verify_expiration] && payload.include?('exp')
161
- raise JWT::ExpiredSignature.new('Signature has expired') unless payload['exp'].to_i > (Time.now.to_i - options[:leeway])
161
+ fail JWT::ExpiredSignature.new('Signature has expired') unless payload['exp'].to_i > (Time.now.to_i - options[:leeway])
162
162
  end
163
163
  if options[:verify_not_before] && payload.include?('nbf')
164
- raise JWT::ImmatureSignature.new('Signature nbf has not been reached') unless payload['nbf'].to_i < (Time.now.to_i + options[:leeway])
164
+ fail JWT::ImmatureSignature.new('Signature nbf has not been reached') unless payload['nbf'].to_i < (Time.now.to_i + options[:leeway])
165
165
  end
166
- if options[:verify_iss] && payload.include?('iss')
167
- raise JWT::InvalidIssuerError.new("Invalid issuer. Expected #{options['iss']}, received #{payload['iss']}") unless payload['iss'].to_s == options['iss'].to_s
166
+ if options[:verify_iss] && options['iss']
167
+ fail JWT::InvalidIssuerError.new("Invalid issuer. Expected #{options['iss']}, received #{payload['iss'] || '<none>'}") unless payload['iss'].to_s == options['iss'].to_s
168
168
  end
169
169
  if options[:verify_iat] && payload.include?('iat')
170
- raise JWT::InvalidIatError.new('Invalid iat') unless (payload['iat'].is_a?(Integer) and payload['iat'].to_i <= Time.now.to_i)
170
+ fail JWT::InvalidIatError.new('Invalid iat') unless payload['iat'].is_a?(Integer) && payload['iat'].to_i <= Time.now.to_i
171
171
  end
172
- if options[:verify_aud] && payload.include?('aud')
172
+ if options[:verify_aud] && options['aud']
173
173
  if payload['aud'].is_a?(Array)
174
- raise JWT::InvalidAudError.new('Invalid audience') unless payload['aud'].include?(options['aud'])
174
+ fail JWT::InvalidAudError.new('Invalid audience') unless payload['aud'].include?(options['aud'].to_s)
175
175
  else
176
- raise JWT::InvalidAudError.new("Invalid audience. Expected #{options['aud']}, received #{payload['aud']}") unless payload['aud'].to_s == options['aud'].to_s
176
+ fail JWT::InvalidAudError.new("Invalid audience. Expected #{options['aud']}, received #{payload['aud'] || '<none>'}") unless payload['aud'].to_s == options['aud'].to_s
177
177
  end
178
178
  end
179
179
  if options[:verify_sub] && payload.include?('sub')
180
- raise JWT::InvalidSubError.new("Invalid subject. Expected #{options['sub']}, received #{payload['sub']}") unless payload['sub'].to_s == options['sub'].to_s
180
+ fail JWT::InvalidSubError.new("Invalid subject. Expected #{options['sub']}, received #{payload['sub']}") unless payload['sub'].to_s == options['sub'].to_s
181
181
  end
182
182
  if options[:verify_jti] && payload.include?('jti')
183
- raise JWT::InvalidJtiError.new('need iat for verify jwt id') unless payload.include?('iat')
184
- raise JWT::InvalidJtiError.new('Not a uniq jwt id') unless options['jti'].to_s == Digest::MD5.hexdigest("#{key}:#{payload['iat']}")
183
+ fail JWT::InvalidJtiError.new('need iat for verify jwt id') unless payload.include?('iat')
184
+ fail JWT::InvalidJtiError.new('Not a uniq jwt id') unless options['jti'].to_s == Digest::MD5.hexdigest("#{key}:#{payload['iat']}")
185
185
  end
186
186
 
187
- return payload,header
187
+ [payload, header]
188
188
  end
189
189
 
190
190
  def signature_algorithm_and_key(header, key, &keyfinder)
191
- if keyfinder
192
- key = keyfinder.call(header)
193
- end
191
+ key = keyfinder.call(header) if keyfinder
194
192
  [header['alg'], key]
195
193
  end
196
194
 
197
195
  def verify_signature(algo, key, signing_input, signature)
198
- begin
199
- if ['HS256', 'HS384', 'HS512'].include?(algo)
200
- raise JWT::VerificationError.new('Signature verification failed') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
201
- elsif ['RS256', 'RS384', 'RS512'].include?(algo)
202
- raise JWT::VerificationError.new('Signature verification failed') unless verify_rsa(algo, key, signing_input, signature)
203
- elsif ['ES256', 'ES384', 'ES512'].include?(algo)
204
- raise JWT::VerificationError.new('Signature verification failed') unless verify_ecdsa(algo, key, signing_input, signature)
205
- else
206
- raise JWT::VerificationError.new('Algorithm not supported')
207
- end
208
- rescue OpenSSL::PKey::PKeyError
209
- raise JWT::VerificationError.new('Signature verification failed')
210
- ensure
211
- OpenSSL.errors.clear
196
+ if ['HS256', 'HS384', 'HS512'].include?(algo)
197
+ fail JWT::VerificationError.new('Signature verification failed') unless secure_compare(signature, sign_hmac(algo, signing_input, key))
198
+ elsif ['RS256', 'RS384', 'RS512'].include?(algo)
199
+ fail JWT::VerificationError.new('Signature verification failed') unless verify_rsa(algo, key, signing_input, signature)
200
+ elsif ['ES256', 'ES384', 'ES512'].include?(algo)
201
+ fail JWT::VerificationError.new('Signature verification failed') unless verify_ecdsa(algo, key, signing_input, signature)
202
+ else
203
+ fail JWT::VerificationError.new('Algorithm not supported')
212
204
  end
205
+ rescue OpenSSL::PKey::PKeyError
206
+ raise JWT::VerificationError.new('Signature verification failed')
207
+ ensure
208
+ OpenSSL.errors.clear
213
209
  end
214
210
 
215
211
  # From devise
@@ -223,4 +219,15 @@ module JWT
223
219
  res == 0
224
220
  end
225
221
 
222
+ def raw_to_asn1(signature, private_key)
223
+ byte_size = (private_key.group.degree + 7) / 8
224
+ r = signature[0..(byte_size - 1)]
225
+ s = signature[byte_size..-1]
226
+ OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
227
+ end
228
+
229
+ def asn1_to_raw(signature, public_key)
230
+ byte_size = (public_key.group.degree + 7) / 8
231
+ OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
232
+ end
226
233
  end
data/lib/jwt/json.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # encoding: utf-8
1
2
  module JWT
3
+ # JSON fallback implementation or ruby 1.8.x
2
4
  module Json
3
5
  if RUBY_VERSION >= '1.9' && !defined?(MultiJson)
4
6
  require 'json'
data/spec/helper.rb CHANGED
@@ -1,2 +1,19 @@
1
+ # encoding: utf-8
1
2
  require 'rspec'
3
+ require 'simplecov'
4
+ require 'simplecov-json'
5
+
6
+ SimpleCov.configure do
7
+ root File.join(File.dirname(__FILE__), '..')
8
+ project_name 'Ruby JWT - Ruby JSON Web Token implementation'
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ SimpleCov::Formatter::JSONFormatter
12
+ ]
13
+
14
+ add_filter 'spec'
15
+ end
16
+
17
+ SimpleCov.start if ENV['COVERAGE']
18
+
2
19
  require "#{File.dirname(__FILE__)}/../lib/jwt.rb"
data/spec/jwt_spec.rb CHANGED
@@ -1,8 +1,9 @@
1
+ # encoding: utf-8
1
2
  require 'helper'
2
3
 
3
4
  describe JWT do
4
5
  before do
5
- @payload = {'foo' => 'bar', 'exp' => Time.now.to_i + 1, 'nbf' => Time.now.to_i - 1 }
6
+ @payload = { 'foo' => 'bar', 'exp' => Time.now.to_i + 1, 'nbf' => Time.now.to_i - 1 }
6
7
  end
7
8
 
8
9
  it 'encodes and decodes JWTs' do
@@ -51,7 +52,7 @@ describe JWT do
51
52
 
52
53
  it 'encodes and decodes JWTs with custom header fields' do
53
54
  private_key = OpenSSL::PKey::RSA.generate(512)
54
- jwt = JWT.encode(@payload, private_key, 'RS256', {'kid' => 'default'})
55
+ jwt = JWT.encode(@payload, private_key, 'RS256', 'kid' => 'default')
55
56
  decoded_payload = JWT.decode(jwt) do |header|
56
57
  expect(header['kid']).to eq('default')
57
58
  private_key.public_key
@@ -68,40 +69,65 @@ describe JWT do
68
69
  end
69
70
 
70
71
  it 'decodes valid JWTs' do
71
- example_payload = {'hello' => 'world'}
72
+ example_payload = { 'hello' => 'world' }
72
73
  example_secret = 'secret'
73
74
  example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
74
75
  decoded_payload = JWT.decode(example_jwt, example_secret)
75
76
  expect(decoded_payload).to include(example_payload)
76
77
  end
77
78
 
79
+ it 'decodes valid ES512 JWTs' do
80
+ example_payload = { 'hello' => 'world' }
81
+ example_jwt = 'eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJoZWxsbyI6IndvcmxkIn0.AQx1MqdTni6KuzfOoedg2-7NUiwe-b88SWbdmviz40GTwrM0Mybp1i1tVtmTSQ91oEXGXBdtwsN6yalzP9J-sp2YATX_Tv4h-BednbdSvYxZsYnUoZ--ZUdL10t7g8Yt3y9hdY_diOjIptcha6ajX8yzkDGYG42iSe3f5LywSuD6FO5c'
82
+ pubkey_pem = "-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAcpkss6wI7PPlxj3t7A1RqMH3nvL4\nL5Tzxze/XeeYZnHqxiX+gle70DlGRMqqOq+PJ6RYX7vK0PJFdiAIXlyPQq0B3KaU\ne86IvFeQSFrJdCc0K8NfiH2G1loIk3fiR+YLqlXk6FAeKtpXJKxR1pCQCAM+vBCs\nmZudf1zCUZ8/4eodlHU=\n-----END PUBLIC KEY-----"
83
+ pubkey = OpenSSL::PKey::EC.new pubkey_pem
84
+ decoded_payload = JWT.decode(example_jwt, pubkey)
85
+ expect(decoded_payload).to include(example_payload)
86
+ end
87
+
78
88
  it 'decodes valid JWTs with iss' do
79
- example_payload = {'hello' => 'world', 'iss' => 'jwtiss'}
89
+ example_payload = { 'hello' => 'world', 'iss' => 'jwtiss' }
80
90
  example_secret = 'secret'
81
91
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0aXNzIn0.nTZkyYfpGUyKULaj45lXw_1gXXjHvGW4h5V7okHdUqQ'
82
- decoded_payload = JWT.decode(example_jwt, example_secret, true, {'iss' => 'jwtiss'})
92
+ decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iss' => 'jwtiss')
83
93
  expect(decoded_payload).to include(example_payload)
84
94
  end
85
95
 
86
- it 'raises invalid issuer' do
87
- # example_payload = {'hello' => 'world', 'iss' => 'jwtiss'}
88
- example_payload2 = {'hello' => 'world'}
96
+ context 'issuer claim verifications' do
97
+ it 'raises invalid issuer when "iss" claim does not match' do
98
+ example_secret = 'secret'
89
99
 
90
- example_secret = 'secret'
100
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0aXNzIn0.nTZkyYfpGUyKULaj45lXw_1gXXjHvGW4h5V7okHdUqQ'
101
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_iss => true, 'iss' => 'jwt_iss') }.to raise_error(JWT::InvalidIssuerError, /Expected jwt_iss, received jwtiss/)
102
+ end
91
103
 
92
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0aXNzIn0.nTZkyYfpGUyKULaj45lXw_1gXXjHvGW4h5V7okHdUqQ'
93
- expect{ JWT.decode(example_jwt, example_secret, true, {:verify_iss => true, 'iss' => 'jwt_iss'}) }.to raise_error(JWT::InvalidIssuerError)
104
+ it 'raises invalid issuer when "iss" claim is missing in payload' do
105
+ example_secret = 'secret'
106
+
107
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIn0.bqxXg9VwcbXKoiWtp-osd0WKPX307RjcN7EuXbdq-CE'
108
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_iss => true, 'iss' => 'jwt_iss') }.to raise_error(JWT::InvalidIssuerError, /received <none>/)
109
+ end
110
+
111
+ it 'does not raise invalid issuer when verify_iss is set to false (default option)' do
112
+ example_secret = 'secret'
113
+
114
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0aXNzIn0.nTZkyYfpGUyKULaj45lXw_1gXXjHvGW4h5V7okHdUqQ'
115
+ expect { JWT.decode(example_jwt, example_secret, true, 'iss' => 'jwt_iss') }.not_to raise_error
116
+ end
117
+
118
+ it 'does not raise invalid issuer when correct "iss" is in payload' do
119
+ example_secret = 'secret'
94
120
 
95
- example_jwt2 = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
96
- decode_payload2 = JWT.decode(example_jwt2, example_secret, true, {'iss' => 'jwt_iss'})
97
- expect(decode_payload2).to include(example_payload2)
121
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0X2lzcyJ9.mwbyRJJZJR1C5lBt8WOLg0ZMuwP9VGDf5HiQtFhd-eA'
122
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_iss => true, 'iss' => 'jwt_iss') }.not_to raise_error
123
+ end
98
124
  end
99
125
 
100
126
  it 'decodes valid JWTs with iat' do
101
- example_payload = {'hello' => 'world', 'iat' => 1425917209}
127
+ example_payload = { 'hello' => 'world', 'iat' => 1425917209 }
102
128
  example_secret = 'secret'
103
129
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5fQ.m4F-Ugo7aLnLunBBO3BeDidyWMx8T9eoJz6FW2rgQhU'
104
- decoded_payload = JWT.decode(example_jwt, example_secret, true, {'iat' => true})
130
+ decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iat' => true)
105
131
  expect(decoded_payload).to include(example_payload)
106
132
  end
107
133
 
@@ -109,14 +135,14 @@ describe JWT do
109
135
  # example_payload = {'hello' => 'world', 'iat' => 'abc'}
110
136
  example_secret = 'secret'
111
137
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoiMTQyNTkxNzIwOSJ9.Mn_vk61xWjIhbXFqAB0nFmNkDiCmfzUgl_LaCKRT6S8'
112
- expect{ JWT.decode(example_jwt, example_secret, true, {:verify_iat => true, 'iat' => 1425917209}) }.to raise_error(JWT::InvalidIatError)
138
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_iat => true, 'iat' => 1425917209) }.to raise_error(JWT::InvalidIatError)
113
139
  end
114
140
 
115
141
  it 'decodes valid JWTs with jti' do
116
- example_payload = {'hello' => 'world', 'iat' => 1425917209, 'jti' => Digest::MD5.hexdigest('secret:1425917209')}
142
+ example_payload = { 'hello' => 'world', 'iat' => 1425917209, 'jti' => Digest::MD5.hexdigest('secret:1425917209') }
117
143
  example_secret = 'secret'
118
144
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
119
- decoded_payload = JWT.decode(example_jwt, example_secret, true, {'jti' => Digest::MD5.hexdigest('secret:1425917209')})
145
+ decoded_payload = JWT.decode(example_jwt, example_secret, true, 'jti' => Digest::MD5.hexdigest('secret:1425917209'))
120
146
  expect(decoded_payload).to include(example_payload)
121
147
  end
122
148
 
@@ -124,7 +150,7 @@ describe JWT do
124
150
  # example_payload = {'hello' => 'world', 'iat' => 1425917209, 'jti' => Digest::MD5.hexdigest('secret:1425917209')}
125
151
  example_secret = 'secret'
126
152
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
127
- expect{ JWT.decode(example_jwt, example_secret, true, {:verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')}) }.to raise_error(JWT::InvalidJtiError)
153
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')) }.to raise_error(JWT::InvalidJtiError)
128
154
  # expect{ JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::InvalidJtiError)
129
155
  end
130
156
 
@@ -132,43 +158,46 @@ describe JWT do
132
158
  # example_payload = {'hello' => 'world', 'jti' => Digest::MD5.hexdigest('secret:1425917209')}
133
159
  example_secret = 'secret'
134
160
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwianRpIjoiNTVjNzc2ZTIxZjdjYmQ4NzljMDZmYWMwMThkYWM0MDIifQ.n0foJCnCM_-_xUvG_TOmR9mYpL2y0UqZOD_gv33djeE'
135
- expect{ JWT.decode(example_jwt, example_secret, true, {:verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')}) }.to raise_error(JWT::InvalidJtiError)
136
- end
161
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')) }.to raise_error(JWT::InvalidJtiError)
162
+ end
163
+
164
+ context 'aud claim verifications' do
165
+ it 'decodes valid JWTs with aud' do
166
+ example_payload = { 'hello' => 'world', 'aud' => 'url:pnd' }
167
+ example_payload2 = { 'hello' => 'world', 'aud' => ['url:pnd', 'aud:yes'] }
168
+ example_secret = 'secret'
169
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
170
+ example_jwt2 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjpbInVybDpwbmQiLCJhdWQ6eWVzIl19.qNPNcT4X9B5uI91rIwbW2bIPTsp8wbRYW3jkZkrmqbQ'
171
+ decoded_payload = JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd')
172
+ decoded_payload2 = JWT.decode(example_jwt2, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd')
173
+ expect(decoded_payload).to include(example_payload)
174
+ expect(decoded_payload2).to include(example_payload2)
175
+ end
137
176
 
138
- it 'decodes valid JWTs with aud' do
139
- example_payload = {'hello' => 'world', 'aud' => 'url:pnd'}
140
- example_payload2 = {'hello' => 'world', 'aud' => ['url:pnd', 'aud:yes']}
141
- example_secret = 'secret'
142
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
143
- example_jwt2 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjpbInVybDpwbmQiLCJhdWQ6eWVzIl19.qNPNcT4X9B5uI91rIwbW2bIPTsp8wbRYW3jkZkrmqbQ'
144
- decoded_payload = JWT.decode(example_jwt, example_secret, true, {'aud' => 'url:pnd'})
145
- decoded_payload2 = JWT.decode(example_jwt2, example_secret, true, {'aud' => 'url:pnd'})
146
- expect(decoded_payload).to include(example_payload)
147
- expect(decoded_payload2).to include(example_payload2)
148
- end
177
+ it 'raises deode exception when aud is invalid' do
178
+ # example_payload = {'hello' => 'world', 'aud' => 'url:pnd'}
179
+ example_secret = 'secret'
180
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
181
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'wrong:aud') }.to raise_error(JWT::InvalidAudError)
182
+ end
149
183
 
150
- it 'raises deode exception when aud is invalid' do
151
- # example_payload = {'hello' => 'world', 'aud' => 'url:pnd'}
152
- example_secret = 'secret'
153
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiYXVkIjoidXJsOnBuZCJ9._gT5veUtNiZD7wLEC6Gd0-nkQV3cl1z8G0zXq8qcd-8'
154
- expect{ JWT.decode(example_jwt, example_secret, true, {:verify_aud => true, 'aud' => 'wrong:aud'}) }.to raise_error(JWT::InvalidAudError)
184
+ it 'raises deode exception when aud is missing' do
185
+ # JWT.encode('hello' => 'world', 'secret')
186
+ example_secret = 'secret'
187
+ example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIn0.bqxXg9VwcbXKoiWtp-osd0WKPX307RjcN7EuXbdq-CE'
188
+ expect { JWT.decode(example_jwt, example_secret, true, :verify_aud => true, 'aud' => 'url:pnd') }.to raise_error(JWT::InvalidAudError)
189
+ end
155
190
  end
156
191
 
157
192
  it 'decodes valid JWTs with sub' do
158
- example_payload = {'hello' => 'world', 'sub' => 'subject'}
193
+ example_payload = { 'hello' => 'world', 'sub' => 'subject' }
159
194
  example_secret = 'secret'
160
195
  example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwic3ViIjoic3ViamVjdCJ9.QUnNVZm4SPB4vP2zY9m1LoUSOx-5oGXBhj7R89D_UtA'
161
- decoded_payload = JWT.decode(example_jwt, example_secret, true, {'sub' => 'subject'})
196
+ decoded_payload = JWT.decode(example_jwt, example_secret, true, 'sub' => 'subject')
162
197
  expect(decoded_payload).to include(example_payload)
163
198
  end
164
199
 
165
- it 'raise decode exception when the sub is invalid' do
166
- # example_payload = {'hello' => 'world', 'sub' => 'subject'}
167
- # TODO: Test not working
168
- example_secret = 'secret'
169
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwic3ViIjoic3ViamVjdCJ9.QUnNVZm4SPB4vP2zY9m1LoUSOx-5oGXBhj7R89D_UtA'
170
- # expect{ JWT.decode(example_jwt, example_secret, true, {:verify_iss => true, 'iss' => 'subject'}) }.to raise_error(JWT::InvalidSubError)
171
- end
200
+ it 'raise decode exception when the sub is invalid'
172
201
 
173
202
  it 'raises decode exception when the token is invalid' do
174
203
  example_secret = 'secret'
@@ -312,7 +341,7 @@ describe JWT do
312
341
  expired_payload['exp'] = Time.now.to_i - 1
313
342
  secret = 'secret'
314
343
  jwt = JWT.encode(expired_payload, secret)
315
- decoded_payload = JWT.decode(jwt, secret, true, {:verify_expiration => false})
344
+ decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
316
345
  expect(decoded_payload).to include(expired_payload)
317
346
  end
318
347
 
@@ -321,7 +350,7 @@ describe JWT do
321
350
  expired_payload['exp'] = Time.now.to_i - 2
322
351
  secret = 'secret'
323
352
  jwt = JWT.encode(expired_payload, secret)
324
- decoded_payload = JWT.decode(jwt, secret, true, {:leeway => 3})
353
+ decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
325
354
  expect(decoded_payload).to include(expired_payload)
326
355
  end
327
356
 
@@ -337,7 +366,7 @@ describe JWT do
337
366
  mature_payload = @payload.clone
338
367
  secret = 'secret'
339
368
  jwt = JWT.encode(mature_payload, secret)
340
- decoded_payload = JWT.decode(jwt, secret, true, {:verify_expiration => false})
369
+ decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
341
370
  expect(decoded_payload).to include(mature_payload)
342
371
  end
343
372
 
@@ -354,7 +383,7 @@ describe JWT do
354
383
  immature_payload['nbf'] = Time.now.to_i + 2
355
384
  secret = 'secret'
356
385
  jwt = JWT.encode(immature_payload, secret)
357
- decoded_payload = JWT.decode(jwt, secret, true, {:verify_not_before => false})
386
+ decoded_payload = JWT.decode(jwt, secret, true, :verify_not_before => false)
358
387
  expect(decoded_payload).to include(immature_payload)
359
388
  end
360
389
 
@@ -363,7 +392,7 @@ describe JWT do
363
392
  immature_payload['nbf'] = Time.now.to_i - 2
364
393
  secret = 'secret'
365
394
  jwt = JWT.encode(immature_payload, secret)
366
- decoded_payload = JWT.decode(jwt, secret, true, {:leeway => 3})
395
+ decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
367
396
  expect(decoded_payload).to include(immature_payload)
368
397
  end
369
398
 
@@ -402,13 +431,13 @@ xwIDAQAB
402
431
  -----END PUBLIC KEY-----
403
432
  PUBKEY
404
433
  jwt = (
405
- 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' +
406
- 'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' +
407
- 'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' +
408
- '20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' +
409
- '0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' +
410
- 'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' +
411
- 'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' +
434
+ 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' \
435
+ 'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' \
436
+ 'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' \
437
+ '20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' \
438
+ '0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' \
439
+ 'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' \
440
+ 'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' \
412
441
  '6hFycH8wj7Su6JqqyEbIVLxE7q7DkAZGaMPkxbTHs1EhSd5_oaKQ6O4xO3ZnnT4'
413
442
  )
414
443
  expect { JWT.decode(jwt, pubkey, true) }.to raise_error(JWT::DecodeError)
@@ -427,7 +456,7 @@ PUBKEY
427
456
  jwt = JWT.encode(@payload, secret)
428
457
  decoded_segments = JWT.decoded_segments(jwt)
429
458
  expect(decoded_segments.size).to eq(4)
430
- expect(decoded_segments[0]).to eq({'typ' => 'JWT', 'alg' => 'HS256'})
459
+ expect(decoded_segments[0]).to eq('typ' => 'JWT', 'alg' => 'HS256')
431
460
  expect(decoded_segments[1]).to eq(@payload)
432
461
  end
433
462
  end
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: 1.5.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Lindsay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-09 00:00:00.000000000 Z
11
+ date: 2015-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: echoe