jwt 1.5.1 → 2.1.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +20 -0
  3. data/.ebert.yml +18 -0
  4. data/.gitignore +11 -0
  5. data/.reek.yml +40 -0
  6. data/.rspec +1 -0
  7. data/.rubocop.yml +98 -0
  8. data/.travis.yml +14 -0
  9. data/CHANGELOG.md +476 -0
  10. data/Gemfile +3 -0
  11. data/LICENSE +7 -0
  12. data/Manifest +3 -1
  13. data/README.md +478 -0
  14. data/Rakefile +8 -15
  15. data/lib/jwt/algos/ecdsa.rb +35 -0
  16. data/lib/jwt/algos/eddsa.rb +23 -0
  17. data/lib/jwt/algos/hmac.rb +33 -0
  18. data/lib/jwt/algos/rsa.rb +19 -0
  19. data/lib/jwt/algos/unsupported.rb +16 -0
  20. data/lib/jwt/decode.rb +49 -0
  21. data/lib/jwt/default_options.rb +15 -0
  22. data/lib/jwt/encode.rb +51 -0
  23. data/lib/jwt/error.rb +16 -0
  24. data/lib/jwt/security_utils.rb +51 -0
  25. data/lib/jwt/signature.rb +50 -0
  26. data/lib/jwt/verify.rb +102 -0
  27. data/lib/jwt/version.rb +24 -0
  28. data/lib/jwt.rb +33 -203
  29. data/ruby-jwt.gemspec +31 -0
  30. data/spec/fixtures/certs/ec256-private.pem +8 -0
  31. data/spec/fixtures/certs/ec256-public.pem +4 -0
  32. data/spec/fixtures/certs/ec256-wrong-private.pem +8 -0
  33. data/spec/fixtures/certs/ec256-wrong-public.pem +4 -0
  34. data/spec/fixtures/certs/ec384-private.pem +9 -0
  35. data/spec/fixtures/certs/ec384-public.pem +5 -0
  36. data/spec/fixtures/certs/ec384-wrong-private.pem +9 -0
  37. data/spec/fixtures/certs/ec384-wrong-public.pem +5 -0
  38. data/spec/fixtures/certs/ec512-private.pem +10 -0
  39. data/spec/fixtures/certs/ec512-public.pem +6 -0
  40. data/spec/fixtures/certs/ec512-wrong-private.pem +10 -0
  41. data/spec/fixtures/certs/ec512-wrong-public.pem +6 -0
  42. data/spec/fixtures/certs/rsa-1024-private.pem +15 -0
  43. data/spec/fixtures/certs/rsa-1024-public.pem +6 -0
  44. data/spec/fixtures/certs/rsa-2048-private.pem +27 -0
  45. data/spec/fixtures/certs/rsa-2048-public.pem +9 -0
  46. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +27 -0
  47. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +9 -0
  48. data/spec/fixtures/certs/rsa-4096-private.pem +51 -0
  49. data/spec/fixtures/certs/rsa-4096-public.pem +14 -0
  50. data/spec/integration/readme_examples_spec.rb +202 -0
  51. data/spec/jwt/verify_spec.rb +232 -0
  52. data/spec/jwt_spec.rb +236 -384
  53. data/spec/spec_helper.rb +28 -0
  54. metadata +187 -26
  55. data/jwt.gemspec +0 -34
  56. data/lib/jwt/json.rb +0 -32
  57. data/spec/helper.rb +0 -19
data/spec/jwt_spec.rb CHANGED
@@ -1,463 +1,315 @@
1
- # encoding: utf-8
2
- require 'helper'
1
+ require 'spec_helper'
2
+ require 'jwt'
3
+ require 'jwt/encode'
4
+ require 'jwt/decode'
3
5
 
4
6
  describe JWT do
5
- before do
6
- @payload = { 'foo' => 'bar', 'exp' => Time.now.to_i + 1, 'nbf' => Time.now.to_i - 1 }
7
- end
8
-
9
- it 'encodes and decodes JWTs' do
10
- secret = 'secret'
11
- jwt = JWT.encode(@payload, secret)
12
- decoded_payload = JWT.decode(jwt, secret)
13
- expect(decoded_payload).to include(@payload)
14
- end
15
-
16
- it 'encodes and decodes JWTs for RSA signatures' do
17
- private_key = OpenSSL::PKey::RSA.generate(512)
18
- jwt = JWT.encode(@payload, private_key, 'RS256')
19
- decoded_payload = JWT.decode(jwt, private_key.public_key)
20
- expect(decoded_payload).to include(@payload)
21
- end
22
-
23
- it 'encodes and decodes JWTs for ECDSA P-256 signatures' do
24
- private_key = OpenSSL::PKey::EC.new('prime256v1')
25
- private_key.generate_key
26
- public_key = OpenSSL::PKey::EC.new(private_key)
27
- public_key.private_key = nil
28
- jwt = JWT.encode(@payload, private_key, 'ES256')
29
- decoded_payload = JWT.decode(jwt, public_key)
30
- expect(decoded_payload).to include(@payload)
7
+ let(:payload) { { 'user_id' => 'some@user.tld' } }
8
+
9
+ let :data do
10
+ {
11
+ :secret => 'My$ecretK3y',
12
+ :rsa_private => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-private.pem'))),
13
+ :rsa_public => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-public.pem'))),
14
+ :wrong_rsa_private => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))),
15
+ :wrong_rsa_public => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))),
16
+ 'ES256_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-private.pem'))),
17
+ 'ES256_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-public.pem'))),
18
+ 'ES384_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec384-private.pem'))),
19
+ 'ES384_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec384-public.pem'))),
20
+ 'ES512_private' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec512-private.pem'))),
21
+ 'ES512_public' => OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec512-public.pem'))),
22
+ 'ED25519_private' => RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF'),
23
+ 'ED25519_public' => RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF').verify_key,
24
+ 'NONE' => 'eyJhbGciOiJub25lIn0.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.',
25
+ 'HS256' => 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.kWOVtIOpWcG7JnyJG0qOkTDbOy636XrrQhMm_8JrRQ8',
26
+ 'HS512256' => 'eyJhbGciOiJIUzUxMjI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Ds_4ibvf7z4QOBoKntEjDfthy3WJ-3rKMspTEcHE2bA',
27
+ 'HS384' => 'eyJhbGciOiJIUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.VuV4j4A1HKhWxCNzEcwc9qVF3frrEu-BRLzvYPkbWO0LENRGy5dOiBQ34remM3XH',
28
+ 'HS512' => 'eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.8zNtCBTJIZTHpZ-BkhR-6sZY1K85Nm5YCKqV3AxRdsBJDt_RR-REH2db4T3Y0uQwNknhrCnZGvhNHrvhDwV1kA',
29
+ 'RS256' => 'eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.eSXvWP4GViiwUALj_-qTxU68I1oM0XjgDsCZBBUri2Ghh9d75QkVDoZ_v872GaqunN5A5xcnBK0-cOq-CR6OwibgJWfOt69GNzw5RrOfQ2mz3QI3NYEq080nF69h8BeqkiaXhI24Q51joEgfa9aj5Y-oitLAmtDPYTm7vTcdGufd6AwD3_3jajKBwkh0LPSeMtbe_5EyS94nFoEF9OQuhJYjUmp7agsBVa8FFEjVw5jEgVqkvERSj5hSY4nEiCAomdVxIKBfykyi0d12cgjhI7mBFwWkPku8XIPGZ7N8vpiSLdM68BnUqIK5qR7NAhtvT7iyLFgOqhZNUQ6Ret5VpQ',
30
+ 'RS384' => 'eyJhbGciOiJSUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Sfgk56moPghtsjaP4so6tOy3I553mgwX-5gByMC6dX8lpeWgsxSeAd_K8IyO7u4lwYOL0DSftnqO1HEOuN1AKyBbDvaTXz3u2xNA2x4NYLdW4AZA6ritbYcKLO5BHTXw5ueMbtA1jjGXP0zI_aK2iJTMBmB8SCF88RYBUH01Tyf4PlLj98pGL-v3prZd6kZkIeRJ3326h04hslcB5HQKmgeBk24QNLIoIC-CD329HPjJ7TtGx01lj-ehTBnwVbBGzYFAyoalV5KgvL_MDOfWPr1OYHnR5s_Fm6_3Vg4u6lBljvHOrmv4Nfx7d8HLgbo8CwH4qn1wm6VQCtuDd-uhRg',
31
+ 'RS512' => 'eyJhbGciOiJSUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.LIIAUEuCkGNdpYguOO5LoW4rZ7ED2POJrB0pmEAAchyTdIK4HKh1jcLxc6KyGwZv40njCgub3y72q6vcQTn7oD0zWFCVQRIDW1911Ii2hRNHuigiPUnrnZh1OQ6z65VZRU6GKs8omoBGU9vrClBU0ODqYE16KxYmE_0n4Xw2h3D_L1LF0IAOtDWKBRDa3QHwZRM9sHsHNsBuD5ye9KzDYN1YALXj64LBfA-DoCKfpVAm9NkRPOyzjR2X2C3TomOSJgqWIVHJucudKDDAZyEbO4RA5pI-UFYy1370p9bRajvtDyoBuLDCzoSkMyQ4L2DnLhx5CbWcnD7Cd3GUmnjjTA',
32
+ 'ES256' => '',
33
+ 'ES384' => '',
34
+ 'ES512' => ''
35
+ }
36
+ end
37
+
38
+ after(:each) do
39
+ expect(OpenSSL.errors).to be_empty
31
40
  end
32
41
 
33
- it 'encodes and decodes JWTs for ECDSA P-384 signatures' do
34
- private_key = OpenSSL::PKey::EC.new('secp384r1')
35
- private_key.generate_key
36
- public_key = OpenSSL::PKey::EC.new(private_key)
37
- public_key.private_key = nil
38
- jwt = JWT.encode(@payload, private_key, 'ES384')
39
- decoded_payload = JWT.decode(jwt, public_key)
40
- expect(decoded_payload).to include(@payload)
41
- end
42
+ context 'alg: NONE' do
43
+ let(:alg) { 'none' }
42
44
 
43
- it 'encodes and decodes JWTs for ECDSA P-521 signatures' do
44
- private_key = OpenSSL::PKey::EC.new('secp521r1')
45
- private_key.generate_key
46
- public_key = OpenSSL::PKey::EC.new(private_key)
47
- public_key.private_key = nil
48
- jwt = JWT.encode(@payload, private_key, 'ES512')
49
- decoded_payload = JWT.decode(jwt, public_key)
50
- expect(decoded_payload).to include(@payload)
51
- end
45
+ it 'should generate a valid token' do
46
+ token = JWT.encode payload, nil, alg
52
47
 
53
- it 'encodes and decodes JWTs with custom header fields' do
54
- private_key = OpenSSL::PKey::RSA.generate(512)
55
- jwt = JWT.encode(@payload, private_key, 'RS256', 'kid' => 'default')
56
- decoded_payload = JWT.decode(jwt) do |header|
57
- expect(header['kid']).to eq('default')
58
- private_key.public_key
48
+ expect(token).to eq data['NONE']
59
49
  end
60
- expect(decoded_payload).to include(@payload)
61
- end
62
50
 
63
- it 'raises encode exception when ECDSA algorithm does not match key' do
64
- private_key = OpenSSL::PKey::EC.new('prime256v1')
65
- private_key.generate_key
66
- expect do
67
- JWT.encode(@payload, private_key, 'ES512')
68
- end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES512 but ES256 signing key was provided')
69
- end
51
+ it 'should decode a valid token' do
52
+ jwt_payload, header = JWT.decode data['NONE'], nil, false
70
53
 
71
- it 'decodes valid JWTs' do
72
- example_payload = { 'hello' => 'world' }
73
- example_secret = 'secret'
74
- example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
75
- decoded_payload = JWT.decode(example_jwt, example_secret)
76
- expect(decoded_payload).to include(example_payload)
77
- end
54
+ expect(header['alg']).to eq alg
55
+ expect(jwt_payload).to eq payload
56
+ end
78
57
 
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
58
+ it 'should display a better error message if payload exp is_a?(Time)' do
59
+ payload['exp'] = Time.now
87
60
 
88
- it 'decodes valid JWTs with iss' do
89
- example_payload = { 'hello' => 'world', 'iss' => 'jwtiss' }
90
- example_secret = 'secret'
91
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaXNzIjoiand0aXNzIn0.nTZkyYfpGUyKULaj45lXw_1gXXjHvGW4h5V7okHdUqQ'
92
- decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iss' => 'jwtiss')
93
- expect(decoded_payload).to include(example_payload)
94
- end
61
+ expect do
62
+ JWT.encode payload, nil, alg
63
+ end.to raise_error JWT::InvalidPayload
64
+ end
95
65
 
96
- context 'issuer claim verifications' do
97
- it 'raises invalid issuer when "iss" claim does not match' do
98
- example_secret = 'secret'
66
+ it 'should display a better error message if payload exp is not an Integer' do
67
+ payload['exp'] = Time.now.to_i.to_s
99
68
 
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/)
69
+ expect do
70
+ JWT.encode payload, nil, alg
71
+ end.to raise_error JWT::InvalidPayload
102
72
  end
73
+ end
103
74
 
104
- it 'raises invalid issuer when "iss" claim is missing in payload' do
105
- example_secret = 'secret'
75
+ %w[HS256 HS512256 HS384 HS512].each do |alg|
76
+ context "alg: #{alg}" do
77
+ it 'should generate a valid token' do
78
+ token = JWT.encode payload, data[:secret], alg
106
79
 
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
80
+ expect(token).to eq data[alg]
81
+ end
110
82
 
111
- it 'does not raise invalid issuer when verify_iss is set to false (default option)' do
112
- example_secret = 'secret'
83
+ it 'should decode a valid token' do
84
+ jwt_payload, header = JWT.decode data[alg], data[:secret], true, algorithm: alg
113
85
 
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
86
+ expect(header['alg']).to eq alg
87
+ expect(jwt_payload).to eq payload
88
+ end
117
89
 
118
- it 'does not raise invalid issuer when correct "iss" is in payload' do
119
- example_secret = 'secret'
90
+ it 'wrong secret should raise JWT::DecodeError' do
91
+ expect do
92
+ JWT.decode data[alg], 'wrong_secret', true, algorithm: alg
93
+ end.to raise_error JWT::VerificationError
94
+ end
120
95
 
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
96
+ it 'wrong secret and verify = false should not raise JWT::DecodeError' do
97
+ expect do
98
+ JWT.decode data[alg], 'wrong_secret', false
99
+ end.not_to raise_error
100
+ end
123
101
  end
124
102
  end
125
103
 
126
- it 'decodes valid JWTs with iat' do
127
- example_payload = { 'hello' => 'world', 'iat' => 1425917209 }
128
- example_secret = 'secret'
129
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5fQ.m4F-Ugo7aLnLunBBO3BeDidyWMx8T9eoJz6FW2rgQhU'
130
- decoded_payload = JWT.decode(example_jwt, example_secret, true, 'iat' => true)
131
- expect(decoded_payload).to include(example_payload)
132
- end
104
+ %w[RS256 RS384 RS512].each do |alg|
105
+ context "alg: #{alg}" do
106
+ it 'should generate a valid token' do
107
+ token = JWT.encode payload, data[:rsa_private], alg
133
108
 
134
- it 'raises decode exception when iat is invalid' do
135
- # example_payload = {'hello' => 'world', 'iat' => 'abc'}
136
- example_secret = 'secret'
137
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoiMTQyNTkxNzIwOSJ9.Mn_vk61xWjIhbXFqAB0nFmNkDiCmfzUgl_LaCKRT6S8'
138
- expect { JWT.decode(example_jwt, example_secret, true, :verify_iat => true, 'iat' => 1425917209) }.to raise_error(JWT::InvalidIatError)
139
- end
109
+ expect(token).to eq data[alg]
110
+ end
140
111
 
141
- it 'decodes valid JWTs with jti' do
142
- example_payload = { 'hello' => 'world', 'iat' => 1425917209, 'jti' => Digest::MD5.hexdigest('secret:1425917209') }
143
- example_secret = 'secret'
144
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
145
- decoded_payload = JWT.decode(example_jwt, example_secret, true, 'jti' => Digest::MD5.hexdigest('secret:1425917209'))
146
- expect(decoded_payload).to include(example_payload)
147
- end
112
+ it 'should decode a valid token' do
113
+ jwt_payload, header = JWT.decode data[alg], data[:rsa_public], true, algorithm: alg
148
114
 
149
- it 'raises decode exception when jti is invalid' do
150
- # example_payload = {'hello' => 'world', 'iat' => 1425917209, 'jti' => Digest::MD5.hexdigest('secret:1425917209')}
151
- example_secret = 'secret'
152
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwiaWF0IjoxNDI1OTE3MjA5LCJqdGkiOiI1NWM3NzZlMjFmN2NiZDg3OWMwNmZhYzAxOGRhYzQwMiJ9.ET0hb-VTUOL3M22oG13ofzvGPLMAncbF8rdNDIqo8tg'
153
- expect { JWT.decode(example_jwt, example_secret, true, :verify_jti => true, 'jti' => Digest::MD5.hexdigest('secret:1425922032')) }.to raise_error(JWT::InvalidJtiError)
154
- # expect{ JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::InvalidJtiError)
155
- end
115
+ expect(header['alg']).to eq alg
116
+ expect(jwt_payload).to eq payload
117
+ end
156
118
 
157
- it 'raises decode exception when jti without iat' do
158
- # example_payload = {'hello' => 'world', 'jti' => Digest::MD5.hexdigest('secret:1425917209')}
159
- example_secret = 'secret'
160
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwianRpIjoiNTVjNzc2ZTIxZjdjYmQ4NzljMDZmYWMwMThkYWM0MDIifQ.n0foJCnCM_-_xUvG_TOmR9mYpL2y0UqZOD_gv33djeE'
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
119
+ it 'wrong key should raise JWT::DecodeError' do
120
+ key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
163
121
 
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
122
+ expect do
123
+ JWT.decode data[alg], key, true, algorithm: alg
124
+ end.to raise_error JWT::DecodeError
125
+ end
176
126
 
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
127
+ it 'wrong key and verify = false should not raise JWT::DecodeError' do
128
+ key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
183
129
 
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)
130
+ expect do
131
+ JWT.decode data[alg], key, false
132
+ end.not_to raise_error
133
+ end
189
134
  end
190
135
  end
191
136
 
192
- it 'decodes valid JWTs with sub' do
193
- example_payload = { 'hello' => 'world', 'sub' => 'subject' }
194
- example_secret = 'secret'
195
- example_jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJoZWxsbyI6IndvcmxkIiwic3ViIjoic3ViamVjdCJ9.QUnNVZm4SPB4vP2zY9m1LoUSOx-5oGXBhj7R89D_UtA'
196
- decoded_payload = JWT.decode(example_jwt, example_secret, true, 'sub' => 'subject')
197
- expect(decoded_payload).to include(example_payload)
198
- end
199
-
200
- it 'raise decode exception when the sub is invalid'
137
+ %w[ED25519].each do |alg|
138
+ context "alg: #{alg}" do
139
+ before(:each) do
140
+ data[alg] = JWT.encode payload, data["#{alg}_private"], alg
141
+ end
201
142
 
202
- it 'raises decode exception when the token is invalid' do
203
- example_secret = 'secret'
204
- # Same as above exmaple with some random bytes replaced
205
- example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHiMomlwIjogIkJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.tvagLDLoaiJKxOKqpBXSEGy7SYSifZhjntgm9ctpyj8'
206
- expect { JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::DecodeError)
207
- end
143
+ let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
208
144
 
209
- it 'raises verification exception with wrong hmac key' do
210
- right_secret = 'foo'
211
- bad_secret = 'bar'
212
- jwt_message = JWT.encode(@payload, right_secret, 'HS256')
213
- expect { JWT.decode(jwt_message, bad_secret) }.to raise_error(JWT::VerificationError)
214
- end
145
+ it 'should generate a valid token' do
146
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
215
147
 
216
- it 'raises decode exception when ECDSA algorithm does not match key' do
217
- right_private_key = OpenSSL::PKey::EC.new('prime256v1')
218
- right_private_key.generate_key
219
- right_public_key = OpenSSL::PKey::EC.new(right_private_key)
220
- right_public_key.private_key = nil
221
- bad_private_key = OpenSSL::PKey::EC.new('secp384r1')
222
- bad_private_key.generate_key
223
- bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
224
- bad_public_key.private_key = nil
225
- jwt = JWT.encode(@payload, right_private_key, 'ES256')
226
- expect do
227
- JWT.decode(jwt, bad_public_key)
228
- end.to raise_error(JWT::IncorrectAlgorithm, 'payload algorithm is ES256 but ES384 verification key was provided')
229
- end
230
-
231
- it 'raises verification exception with wrong rsa key' do
232
- right_private_key = OpenSSL::PKey::RSA.generate(512)
233
- bad_private_key = OpenSSL::PKey::RSA.generate(512)
234
- jwt = JWT.encode(@payload, right_private_key, 'RS256')
235
- expect { JWT.decode(jwt, bad_private_key.public_key) }.to raise_error(JWT::VerificationError)
236
- end
148
+ expect(header['alg']).to eq alg
149
+ expect(jwt_payload).to eq payload
150
+ end
237
151
 
238
- it 'raises verification exception with wrong ECDSA key' do
239
- right_private_key = OpenSSL::PKey::EC.new('prime256v1')
240
- right_private_key.generate_key
241
- bad_private_key = OpenSSL::PKey::EC.new('prime256v1')
242
- bad_private_key.generate_key
243
- bad_public_key = OpenSSL::PKey::EC.new(bad_private_key)
244
- bad_public_key.private_key = nil
245
- jwt = JWT.encode(@payload, right_private_key, 'ES256')
246
- expect { JWT.decode(jwt, bad_public_key) }.to raise_error(JWT::VerificationError)
247
- end
152
+ it 'should decode a valid token' do
153
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
248
154
 
249
- it 'raises decode exception with invalid signature' do
250
- example_secret = 'secret'
251
- example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.'
252
- expect { JWT.decode(example_jwt, example_secret) }.to raise_error(JWT::DecodeError)
253
- end
155
+ expect(header['alg']).to eq alg
156
+ expect(jwt_payload).to eq payload
157
+ end
254
158
 
255
- it 'raises decode exception with nonexistent header' do
256
- expect { JWT.decode('..stuff') }.to raise_error(JWT::DecodeError)
257
- end
159
+ it 'wrong key should raise JWT::DecodeError' do
160
+ expect do
161
+ JWT.decode data[alg], wrong_key
162
+ end.to raise_error JWT::DecodeError
163
+ end
258
164
 
259
- it 'raises decode exception with nonexistent payload' do
260
- expect { JWT.decode('eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9..stuff') }.to raise_error(JWT::DecodeError)
165
+ it 'wrong key and verify = false should not raise JWT::DecodeError' do
166
+ expect do
167
+ JWT.decode data[alg], wrong_key, false
168
+ end.not_to raise_error
169
+ end
170
+ end
261
171
  end
172
+ %w[ES256 ES384 ES512].each do |alg|
173
+ context "alg: #{alg}" do
174
+ before(:each) do
175
+ data[alg] = JWT.encode payload, data["#{alg}_private"], alg
176
+ end
262
177
 
263
- it 'raises decode exception with nil jwt' do
264
- expect { JWT.decode(nil) }.to raise_error(JWT::DecodeError)
265
- end
178
+ let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
266
179
 
267
- it 'allows decoding without key' do
268
- right_secret = 'foo'
269
- bad_secret = 'bar'
270
- jwt = JWT.encode(@payload, right_secret)
271
- decoded_payload = JWT.decode(jwt, bad_secret, false)
272
- expect(decoded_payload).to include(@payload)
273
- end
180
+ it 'should generate a valid token' do
181
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
274
182
 
275
- it 'checks the key when verify is truthy' do
276
- right_secret = 'foo'
277
- bad_secret = 'bar'
278
- jwt = JWT.encode(@payload, right_secret)
279
- verify = 'yes' =~ /^y/i
280
- expect { JWT.decode(jwt, bad_secret, verify) }.to raise_error(JWT::DecodeError)
281
- end
183
+ expect(header['alg']).to eq alg
184
+ expect(jwt_payload).to eq payload
185
+ end
282
186
 
283
- it 'raises exception on unsupported crypto algorithm' do
284
- expect { JWT.encode(@payload, 'secret', 'HS1024') }.to raise_error(NotImplementedError)
285
- end
187
+ it 'should decode a valid token' do
188
+ jwt_payload, header = JWT.decode data[alg], data["#{alg}_public"], true, algorithm: alg
286
189
 
287
- it 'raises exception when decoded with a different algorithm than it was encoded with' do
288
- jwt = JWT.encode(@payload, 'foo', 'HS384')
289
- expect { JWT.decode(jwt, 'foo', true, :algorithm => 'HS512') }.to raise_error(JWT::IncorrectAlgorithm)
290
- end
190
+ expect(header['alg']).to eq alg
191
+ expect(jwt_payload).to eq payload
192
+ end
291
193
 
292
- it 'does not raise exception when encoded with the expected algorithm' do
293
- jwt = JWT.encode(@payload, 'foo', 'HS512')
294
- JWT.decode(jwt, 'foo', true, :algorithm => 'HS512')
295
- end
194
+ it 'wrong key should raise JWT::DecodeError' do
195
+ expect do
196
+ JWT.decode data[alg], wrong_key
197
+ end.to raise_error JWT::DecodeError
198
+ end
296
199
 
297
- it 'encodes and decodes plaintext JWTs' do
298
- jwt = JWT.encode(@payload, nil, nil)
299
- expect(jwt.split('.').length).to eq(2)
300
- decoded_payload = JWT.decode(jwt, nil, nil)
301
- expect(decoded_payload).to include(@payload)
200
+ it 'wrong key and verify = false should not raise JWT::DecodeError' do
201
+ expect do
202
+ JWT.decode data[alg], wrong_key, false
203
+ end.not_to raise_error
204
+ end
205
+ end
302
206
  end
303
207
 
304
- it 'requires a signature segment when verify is truthy' do
305
- jwt = JWT.encode(@payload, nil, nil)
306
- expect(jwt.split('.').length).to eq(2)
307
- expect { JWT.decode(jwt, nil, true) }.to raise_error(JWT::DecodeError)
308
- end
208
+ context 'Invalid' do
209
+ it 'algorithm should raise NotImplementedError' do
210
+ expect do
211
+ JWT.encode payload, 'secret', 'HS255'
212
+ end.to raise_error NotImplementedError
213
+ end
309
214
 
310
- it 'does not use == to compare digests' do
311
- secret = 'secret'
312
- jwt = JWT.encode(@payload, secret)
313
- crypto_segment = jwt.split('.').last
215
+ it 'ECDSA curve_name should raise JWT::IncorrectAlgorithm' do
216
+ key = OpenSSL::PKey::EC.new 'secp256k1'
217
+ key.generate_key
314
218
 
315
- signature = JWT.base64url_decode(crypto_segment)
316
- expect(signature).not_to receive('==')
317
- expect(JWT).to receive(:base64url_decode).with(crypto_segment).once.and_return(signature)
318
- expect(JWT).to receive(:base64url_decode).at_least(:once).and_call_original
219
+ expect do
220
+ JWT.encode payload, key, 'ES256'
221
+ end.to raise_error JWT::IncorrectAlgorithm
319
222
 
320
- JWT.decode(jwt, secret)
321
- end
223
+ token = JWT.encode payload, data['ES256_private'], 'ES256'
224
+ key.private_key = nil
322
225
 
323
- it 'raises error when expired' do
324
- expired_payload = @payload.clone
325
- expired_payload['exp'] = Time.now.to_i - 1
326
- secret = 'secret'
327
- jwt = JWT.encode(expired_payload, secret)
328
- expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ExpiredSignature)
226
+ expect do
227
+ JWT.decode token, key
228
+ end.to raise_error JWT::IncorrectAlgorithm
229
+ end
329
230
  end
330
231
 
331
- it 'raise ExpiredSignature even when exp claims is a string' do
332
- expired_payload = @payload.clone
333
- expired_payload['exp'] = (Time.now.to_i).to_s
334
- secret = 'secret'
335
- jwt = JWT.encode(expired_payload, secret)
336
- expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ExpiredSignature)
337
- end
232
+ context 'Verify' do
233
+ context 'algorithm' do
234
+ it 'should raise JWT::IncorrectAlgorithm on mismatch' do
235
+ token = JWT.encode payload, data[:secret], 'HS256'
338
236
 
339
- it 'performs normal decode with skipped expiration check' do
340
- expired_payload = @payload.clone
341
- expired_payload['exp'] = Time.now.to_i - 1
342
- secret = 'secret'
343
- jwt = JWT.encode(expired_payload, secret)
344
- decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
345
- expect(decoded_payload).to include(expired_payload)
346
- end
237
+ expect do
238
+ JWT.decode token, data[:secret], true, algorithm: 'HS384'
239
+ end.to raise_error JWT::IncorrectAlgorithm
347
240
 
348
- it 'performs normal decode using leeway' do
349
- expired_payload = @payload.clone
350
- expired_payload['exp'] = Time.now.to_i - 2
351
- secret = 'secret'
352
- jwt = JWT.encode(expired_payload, secret)
353
- decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
354
- expect(decoded_payload).to include(expired_payload)
355
- end
241
+ expect do
242
+ JWT.decode token, data[:secret], true, algorithm: 'HS256'
243
+ end.not_to raise_error
244
+ end
356
245
 
357
- it 'raises error when before nbf' do
358
- immature_payload = @payload.clone
359
- immature_payload['nbf'] = Time.now.to_i + 1
360
- secret = 'secret'
361
- jwt = JWT.encode(immature_payload, secret)
362
- expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ImmatureSignature)
363
- end
246
+ it 'should raise JWT::IncorrectAlgorithm when algorithms array does not contain algorithm' do
247
+ token = JWT.encode payload, data[:secret], 'HS512'
364
248
 
365
- it 'doesnt raise error when after nbf' do
366
- mature_payload = @payload.clone
367
- secret = 'secret'
368
- jwt = JWT.encode(mature_payload, secret)
369
- decoded_payload = JWT.decode(jwt, secret, true, :verify_expiration => false)
370
- expect(decoded_payload).to include(mature_payload)
371
- end
249
+ expect do
250
+ JWT.decode token, data[:secret], true, algorithms: ['HS384']
251
+ end.to raise_error JWT::IncorrectAlgorithm
372
252
 
373
- it 'raise ImmatureSignature even when nbf claim is a string' do
374
- immature_payload = @payload.clone
375
- immature_payload['nbf'] = (Time.now.to_i).to_s
376
- secret = 'secret'
377
- jwt = JWT.encode(immature_payload, secret)
378
- expect { JWT.decode(jwt, secret) }.to raise_error(JWT::ImmatureSignature)
379
- end
253
+ expect do
254
+ JWT.decode token, data[:secret], true, algorithms: ['HS512', 'HS384']
255
+ end.not_to raise_error
256
+ end
380
257
 
381
- it 'performs normal decode with skipped not before check' do
382
- immature_payload = @payload.clone
383
- immature_payload['nbf'] = Time.now.to_i + 2
384
- secret = 'secret'
385
- jwt = JWT.encode(immature_payload, secret)
386
- decoded_payload = JWT.decode(jwt, secret, true, :verify_not_before => false)
387
- expect(decoded_payload).to include(immature_payload)
388
- end
258
+ context 'no algorithm provided' do
259
+ it 'should use the default decode algorithm' do
260
+ token = JWT.encode payload, data[:rsa_public].to_s
389
261
 
390
- it 'performs normal decode using leeway' do
391
- immature_payload = @payload.clone
392
- immature_payload['nbf'] = Time.now.to_i - 2
393
- secret = 'secret'
394
- jwt = JWT.encode(immature_payload, secret)
395
- decoded_payload = JWT.decode(jwt, secret, true, :leeway => 3)
396
- expect(decoded_payload).to include(immature_payload)
397
- end
262
+ jwt_payload, header = JWT.decode token, data[:rsa_public].to_s
398
263
 
399
- describe 'secure comparison' do
400
- it 'returns true if strings are equal' do
401
- expect(JWT.secure_compare('Foo', 'Foo')).to be true
264
+ expect(header['alg']).to eq 'HS256'
265
+ expect(jwt_payload).to eq payload
266
+ end
267
+ end
402
268
  end
403
269
 
404
- it 'returns false if either input is nil or empty' do
405
- [nil, ''].each do |bad|
406
- expect(JWT.secure_compare(bad, 'Foo')).to be false
407
- expect(JWT.secure_compare('Foo', bad)).to be false
270
+ context 'issuer claim' do
271
+ let(:iss) { 'ruby-jwt-gem' }
272
+ let(:invalid_token) { JWT.encode payload, data[:secret] }
273
+
274
+ let :token do
275
+ iss_payload = payload.merge(iss: iss)
276
+ JWT.encode iss_payload, data[:secret]
408
277
  end
409
- end
410
278
 
411
- it 'retuns false if the strings are different' do
412
- expect(JWT.secure_compare('Foo', 'Bar')).to be false
279
+ it 'if verify_iss is set to false (default option) should not raise JWT::InvalidIssuerError' do
280
+ expect do
281
+ JWT.decode token, data[:secret], true, iss: iss, algorithm: 'HS256'
282
+ end.not_to raise_error
283
+ end
413
284
  end
414
285
  end
415
286
 
416
- # no method should leave OpenSSL.errors populated
417
- after do
418
- expect(OpenSSL.errors).to be_empty
287
+ context 'Base64' do
288
+ it 'urlsafe replace + / with - _' do
289
+ allow(Base64).to receive(:encode64) { 'string+with/non+url-safe/characters_' }
290
+ expect(JWT::Encode.base64url_encode('foo')).to eq('string-with_non-url-safe_characters_')
291
+ end
419
292
  end
420
293
 
421
- it 'raise exception on invalid signature' do
422
- pubkey = OpenSSL::PKey::RSA.new(<<-PUBKEY)
423
- -----BEGIN PUBLIC KEY-----
424
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCaY7425h964bjaoLeUm
425
- SlZ8sK7VtVk9zHbGmZh2ygGYwfuUf2bmMye2Ofv99yDE/rd4loVIAcu7RVvDRgHq
426
- 3/CZTnIrSvHsiJQsHBNa3d+F1ihPfzURzf1M5k7CFReBj2SBXhDXd57oRfBQj12w
427
- CVhhwP6kGTAWuoppbIIIBfNF2lE/Nvm7lVVYQqL9xOrP/AQ4xRbpQlB8Ll9sO9Or
428
- SvbWhCDa/LMOWxHdmrcJi6XoSg1vnOyCoKbyAoauTt/XqdkHbkDdQ6HFbJieu9il
429
- LDZZNliPhfENuKeC2MCGVXTEu8Cqhy1w6e4axavLlXoYf4laJIZ/e7au8SqDbY0B
430
- xwIDAQAB
431
- -----END PUBLIC KEY-----
432
- PUBKEY
433
- jwt = (
434
- 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiY' \
435
- 'XVkIjoiMTA2MDM1Nzg5MTY4OC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSI' \
436
- 'sImNpZCI6IjEwNjAzNTc4OTE2ODguYXBwcy5nb29nbGV1c2VyY29udGVudC5jb' \
437
- '20iLCJpZCI6IjExNjQ1MjgyNDMwOTg1Njc4MjE2MyIsInRva2VuX2hhc2giOiJ' \
438
- '0Z2hEOUo4bjhWME4ydmN3NmVNaWpnIiwiaWF0IjoxMzIwNjcwOTc4LCJleHAiO' \
439
- 'jEzMjA2NzQ4Nzh9.D8x_wirkxDElqKdJBcsIws3Ogesk38okz6MN7zqC7nEAA7' \
440
- 'wcy1PxsROY1fmBvXSer0IQesAqOW-rPOCNReSn-eY8d53ph1x2HAF-AzEi3GOl' \
441
- '6hFycH8wj7Su6JqqyEbIVLxE7q7DkAZGaMPkxbTHs1EhSd5_oaKQ6O4xO3ZnnT4'
442
- )
443
- expect { JWT.decode(jwt, pubkey, true) }.to raise_error(JWT::DecodeError)
294
+ it 'should not verify token even if the payload has claims' do
295
+ head = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
296
+ load = 'eyJ1c2VyX2lkIjo1NCwiZXhwIjoxNTA0MzkwODA0fQ'
297
+ sign = 'Skpi6FfYMbZ-DwW9ocyRIosNMdPMAIWRLYxRO68GTQk'
298
+
299
+ expect do
300
+ JWT.decode([head, load, sign].join('.'), '', false)
301
+ end.not_to raise_error
444
302
  end
445
303
 
446
- describe 'urlsafe base64 encoding' do
447
- it 'replaces + and / with - and _' do
448
- allow(Base64).to receive(:encode64) { 'string+with/non+url-safe/characters_' }
449
- expect(JWT.base64url_encode('foo')).to eq('string-with_non-url-safe_characters_')
450
- end
304
+ it 'should not raise InvalidPayload exception if payload is an array' do
305
+ expect do
306
+ JWT.encode(['my', 'payload'], 'secret')
307
+ end.not_to raise_error
451
308
  end
452
309
 
453
- describe 'decoded_segments' do
454
- it 'allows access to the decoded header and payload' do
455
- secret = 'secret'
456
- jwt = JWT.encode(@payload, secret)
457
- decoded_segments = JWT.decoded_segments(jwt)
458
- expect(decoded_segments.size).to eq(4)
459
- expect(decoded_segments[0]).to eq('typ' => 'JWT', 'alg' => 'HS256')
460
- expect(decoded_segments[1]).to eq(@payload)
461
- end
310
+ it 'should encode string payloads' do
311
+ expect do
312
+ JWT.encode 'Hello World', 'secret'
313
+ end.not_to raise_error
462
314
  end
463
315
  end