jwtb 2.0.0.beta2.bsk1

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +20 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +5 -0
  6. data/.travis.yml +13 -0
  7. data/CHANGELOG.md +411 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +7 -0
  10. data/Manifest +8 -0
  11. data/README.md +443 -0
  12. data/Rakefile +11 -0
  13. data/jwtb.gemspec +31 -0
  14. data/lib/jwtb.rb +67 -0
  15. data/lib/jwtb/decode.rb +45 -0
  16. data/lib/jwtb/default_options.rb +14 -0
  17. data/lib/jwtb/encode.rb +51 -0
  18. data/lib/jwtb/error.rb +15 -0
  19. data/lib/jwtb/signature.rb +146 -0
  20. data/lib/jwtb/verify.rb +84 -0
  21. data/lib/jwtb/version.rb +24 -0
  22. data/spec/fixtures/certs/ec256-private.pem +8 -0
  23. data/spec/fixtures/certs/ec256-public.pem +4 -0
  24. data/spec/fixtures/certs/ec256-wrong-private.pem +8 -0
  25. data/spec/fixtures/certs/ec256-wrong-public.pem +4 -0
  26. data/spec/fixtures/certs/ec384-private.pem +9 -0
  27. data/spec/fixtures/certs/ec384-public.pem +5 -0
  28. data/spec/fixtures/certs/ec384-wrong-private.pem +9 -0
  29. data/spec/fixtures/certs/ec384-wrong-public.pem +5 -0
  30. data/spec/fixtures/certs/ec512-private.pem +10 -0
  31. data/spec/fixtures/certs/ec512-public.pem +6 -0
  32. data/spec/fixtures/certs/ec512-wrong-private.pem +10 -0
  33. data/spec/fixtures/certs/ec512-wrong-public.pem +6 -0
  34. data/spec/fixtures/certs/rsa-1024-private.pem +15 -0
  35. data/spec/fixtures/certs/rsa-1024-public.pem +6 -0
  36. data/spec/fixtures/certs/rsa-2048-private.pem +27 -0
  37. data/spec/fixtures/certs/rsa-2048-public.pem +9 -0
  38. data/spec/fixtures/certs/rsa-2048-wrong-private.pem +27 -0
  39. data/spec/fixtures/certs/rsa-2048-wrong-public.pem +9 -0
  40. data/spec/fixtures/certs/rsa-4096-private.pem +51 -0
  41. data/spec/fixtures/certs/rsa-4096-public.pem +14 -0
  42. data/spec/integration/readme_examples_spec.rb +216 -0
  43. data/spec/jwtb/verify_spec.rb +190 -0
  44. data/spec/jwtb_spec.rb +233 -0
  45. data/spec/spec_helper.rb +28 -0
  46. metadata +225 -0
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+ require 'jwtb/verify'
4
+
5
+ module JWTB
6
+ RSpec.describe Verify do
7
+ let(:base_payload) { { 'user_id' => 'some@user.tld' } }
8
+ let(:options) { { leeway: 0 } }
9
+
10
+ context '.verify_aud(payload, options)' do
11
+ let(:scalar_aud) { 'ruby-jwt-aud' }
12
+ let(:array_aud) { %w(ruby-jwt-aud test-aud ruby-ruby-ruby) }
13
+ let(:scalar_payload) { base_payload.merge('aud' => scalar_aud) }
14
+ let(:array_payload) { base_payload.merge('aud' => array_aud) }
15
+
16
+ it 'must raise JWTB::InvalidAudError when the singular audience does not match' do
17
+ expect do
18
+ Verify.verify_aud(scalar_payload, options.merge(aud: 'no-match'))
19
+ end.to raise_error JWTB::InvalidAudError
20
+ end
21
+
22
+ it 'must raise JWTB::InvalidAudError when the payload has an array and none match the supplied value' do
23
+ expect do
24
+ Verify.verify_aud(array_payload, options.merge(aud: 'no-match'))
25
+ end.to raise_error JWTB::InvalidAudError
26
+ end
27
+
28
+ it 'must allow a matching singular audience to pass' do
29
+ Verify.verify_aud(scalar_payload, options.merge(aud: scalar_aud))
30
+ end
31
+
32
+ it 'must allow an array with any value matching the one in the options' do
33
+ Verify.verify_aud(array_payload, options.merge(aud: array_aud.first))
34
+ end
35
+
36
+ it 'must allow an array with any value matching any value in the options array' do
37
+ Verify.verify_aud(array_payload, options.merge(aud: array_aud))
38
+ end
39
+
40
+ it 'must allow a singular audience payload matching any value in the options array' do
41
+ Verify.verify_aud(scalar_payload, options.merge(aud: array_aud))
42
+ end
43
+ end
44
+
45
+ context '.verify_expiration(payload, options)' do
46
+ let(:leeway) { 10 }
47
+ let(:payload) { base_payload.merge('exp' => (Time.now.to_i - 5)) }
48
+
49
+ it 'must raise JWTB::ExpiredSignature when the token has expired' do
50
+ expect do
51
+ Verify.verify_expiration(payload, options)
52
+ end.to raise_error JWTB::ExpiredSignature
53
+ end
54
+
55
+ it 'must allow some leeway in the expiration when global leeway is configured' do
56
+ Verify.verify_expiration(payload, options.merge(leeway: 10))
57
+ end
58
+
59
+ it 'must allow some leeway in the expiration when exp_leeway is configured' do
60
+ Verify.verify_expiration(payload, options.merge(exp_leeway: 10))
61
+ end
62
+
63
+ it 'must be expired if the exp claim equals the current time' do
64
+ payload['exp'] = Time.now.to_i
65
+
66
+ expect do
67
+ Verify.verify_expiration(payload, options)
68
+ end.to raise_error JWTB::ExpiredSignature
69
+ end
70
+ end
71
+
72
+ context '.verify_iat(payload, options)' do
73
+ let(:iat) { Time.now.to_f }
74
+ let(:payload) { base_payload.merge('iat' => iat) }
75
+
76
+ it 'must allow a valid iat' do
77
+ Verify.verify_iat(payload, options)
78
+ end
79
+
80
+ it 'must allow configured leeway' do
81
+ Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(leeway: 70))
82
+ end
83
+
84
+ it 'must allow configured iat_leeway' do
85
+ Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(iat_leeway: 70))
86
+ end
87
+
88
+ it 'must properly handle integer times' do
89
+ Verify.verify_iat(payload.merge('iat' => Time.now.to_i), options)
90
+ end
91
+
92
+ it 'must raise JWTB::InvalidIatError when the iat value is not Numeric' do
93
+ expect do
94
+ Verify.verify_iat(payload.merge('iat' => 'not a number'), options)
95
+ end.to raise_error JWTB::InvalidIatError
96
+ end
97
+
98
+ it 'must raise JWTB::InvalidIatError when the iat value is in the future' do
99
+ expect do
100
+ Verify.verify_iat(payload.merge('iat' => (iat + 120)), options)
101
+ end.to raise_error JWTB::InvalidIatError
102
+ end
103
+ end
104
+
105
+ context '.verify_iss(payload, options)' do
106
+ let(:iss) { 'ruby-jwt-gem' }
107
+ let(:payload) { base_payload.merge('iss' => iss) }
108
+
109
+ let(:invalid_token) { JWTB.encode base_payload, payload[:secret] }
110
+
111
+ it 'must raise JWTB::InvalidIssuerError when the configured issuer does not match the payload issuer' do
112
+ expect do
113
+ Verify.verify_iss(payload, options.merge(iss: 'mismatched-issuer'))
114
+ end.to raise_error JWTB::InvalidIssuerError
115
+ end
116
+
117
+ it 'must raise JWTB::InvalidIssuerError when the payload does not include an issuer' do
118
+ expect do
119
+ Verify.verify_iss(base_payload, options.merge(iss: iss))
120
+ end.to raise_error(JWTB::InvalidIssuerError, /received <none>/)
121
+ end
122
+
123
+ it 'must allow a matching issuer to pass' do
124
+ Verify.verify_iss(payload, options.merge(iss: iss))
125
+ end
126
+ end
127
+
128
+ context '.verify_jti(payload, options)' do
129
+ let(:payload) { base_payload.merge('jti' => 'some-random-uuid-or-whatever') }
130
+
131
+ it 'must allow any jti when the verfy_jti key in the options is truthy but not a proc' do
132
+ Verify.verify_jti(payload, options.merge(verify_jti: true))
133
+ end
134
+
135
+ it 'must raise JWTB::InvalidJtiError when the jti is missing' do
136
+ expect do
137
+ Verify.verify_jti(base_payload, options)
138
+ end.to raise_error JWTB::InvalidJtiError, /missing/i
139
+ end
140
+
141
+ it 'must raise JWTB::InvalidJtiError when the jti is an empty string' do
142
+ expect do
143
+ Verify.verify_jti(base_payload.merge('jti' => ' '), options)
144
+ end.to raise_error JWTB::InvalidJtiError, /missing/i
145
+ end
146
+
147
+ it 'must raise JWTB::InvalidJtiError when verify_jti proc returns false' do
148
+ expect do
149
+ Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti) { false }))
150
+ end.to raise_error JWTB::InvalidJtiError, /invalid/i
151
+ end
152
+
153
+ it 'true proc should not raise JWTB::InvalidJtiError' do
154
+ Verify.verify_jti(payload, options.merge(verify_jti: ->(_jti) { true }))
155
+ end
156
+ end
157
+
158
+ context '.verify_not_before(payload, options)' do
159
+ let(:payload) { base_payload.merge('nbf' => (Time.now.to_i + 5)) }
160
+
161
+ it 'must raise JWTB::ImmatureSignature when the nbf in the payload is in the future' do
162
+ expect do
163
+ Verify.verify_not_before(payload, options)
164
+ end.to raise_error JWTB::ImmatureSignature
165
+ end
166
+
167
+ it 'must allow some leeway in the token age when global leeway is configured' do
168
+ Verify.verify_not_before(payload, options.merge(leeway: 10))
169
+ end
170
+
171
+ it 'must allow some leeway in the token age when nbf_leeway is configured' do
172
+ Verify.verify_not_before(payload, options.merge(nbf_leeway: 10))
173
+ end
174
+ end
175
+
176
+ context '.verify_sub(payload, options)' do
177
+ let(:sub) { 'ruby jwt subject' }
178
+
179
+ it 'must raise JWTB::InvalidSubError when the subjects do not match' do
180
+ expect do
181
+ Verify.verify_sub(base_payload.merge('sub' => 'not-a-match'), options.merge(sub: sub))
182
+ end.to raise_error JWTB::InvalidSubError
183
+ end
184
+
185
+ it 'must allow a matching sub' do
186
+ Verify.verify_sub(base_payload.merge('sub' => sub), options.merge(sub: sub))
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,233 @@
1
+ require 'spec_helper'
2
+ require 'jwtb'
3
+ require 'jwtb/encode'
4
+ require 'jwtb/decode'
5
+
6
+ describe JWTB do
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
+ 'NONE' => 'eyJhbGciOiJub25lIn0.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.',
23
+ 'HS256' => 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.kWOVtIOpWcG7JnyJG0qOkTDbOy636XrrQhMm_8JrRQ8',
24
+ 'HS512256' => 'eyJhbGciOiJIUzUxMjI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Ds_4ibvf7z4QOBoKntEjDfthy3WJ-3rKMspTEcHE2bA',
25
+ 'HS384' => 'eyJhbGciOiJIUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.VuV4j4A1HKhWxCNzEcwc9qVF3frrEu-BRLzvYPkbWO0LENRGy5dOiBQ34remM3XH',
26
+ 'HS512' => 'eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.8zNtCBTJIZTHpZ-BkhR-6sZY1K85Nm5YCKqV3AxRdsBJDt_RR-REH2db4T3Y0uQwNknhrCnZGvhNHrvhDwV1kA',
27
+ 'RS256' => 'eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.eSXvWP4GViiwUALj_-qTxU68I1oM0XjgDsCZBBUri2Ghh9d75QkVDoZ_v872GaqunN5A5xcnBK0-cOq-CR6OwibgJWfOt69GNzw5RrOfQ2mz3QI3NYEq080nF69h8BeqkiaXhI24Q51joEgfa9aj5Y-oitLAmtDPYTm7vTcdGufd6AwD3_3jajKBwkh0LPSeMtbe_5EyS94nFoEF9OQuhJYjUmp7agsBVa8FFEjVw5jEgVqkvERSj5hSY4nEiCAomdVxIKBfykyi0d12cgjhI7mBFwWkPku8XIPGZ7N8vpiSLdM68BnUqIK5qR7NAhtvT7iyLFgOqhZNUQ6Ret5VpQ',
28
+ 'RS384' => 'eyJhbGciOiJSUzM4NCJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.Sfgk56moPghtsjaP4so6tOy3I553mgwX-5gByMC6dX8lpeWgsxSeAd_K8IyO7u4lwYOL0DSftnqO1HEOuN1AKyBbDvaTXz3u2xNA2x4NYLdW4AZA6ritbYcKLO5BHTXw5ueMbtA1jjGXP0zI_aK2iJTMBmB8SCF88RYBUH01Tyf4PlLj98pGL-v3prZd6kZkIeRJ3326h04hslcB5HQKmgeBk24QNLIoIC-CD329HPjJ7TtGx01lj-ehTBnwVbBGzYFAyoalV5KgvL_MDOfWPr1OYHnR5s_Fm6_3Vg4u6lBljvHOrmv4Nfx7d8HLgbo8CwH4qn1wm6VQCtuDd-uhRg',
29
+ 'RS512' => 'eyJhbGciOiJSUzUxMiJ9.eyJ1c2VyX2lkIjoic29tZUB1c2VyLnRsZCJ9.LIIAUEuCkGNdpYguOO5LoW4rZ7ED2POJrB0pmEAAchyTdIK4HKh1jcLxc6KyGwZv40njCgub3y72q6vcQTn7oD0zWFCVQRIDW1911Ii2hRNHuigiPUnrnZh1OQ6z65VZRU6GKs8omoBGU9vrClBU0ODqYE16KxYmE_0n4Xw2h3D_L1LF0IAOtDWKBRDa3QHwZRM9sHsHNsBuD5ye9KzDYN1YALXj64LBfA-DoCKfpVAm9NkRPOyzjR2X2C3TomOSJgqWIVHJucudKDDAZyEbO4RA5pI-UFYy1370p9bRajvtDyoBuLDCzoSkMyQ4L2DnLhx5CbWcnD7Cd3GUmnjjTA',
30
+ 'ES256' => '',
31
+ 'ES384' => '',
32
+ 'ES512' => ''
33
+ }
34
+ end
35
+
36
+ after(:each) do
37
+ expect(OpenSSL.errors).to be_empty
38
+ end
39
+
40
+ context 'alg: NONE' do
41
+ let(:alg) { 'none' }
42
+
43
+ it 'should generate a valid token' do
44
+ token = JWTB.encode payload, nil, alg
45
+
46
+ expect(token).to eq data['NONE']
47
+ end
48
+
49
+ it 'should decode a valid token' do
50
+ jwt_payload, header = JWTB.decode data['NONE'], nil, false
51
+
52
+ expect(header['alg']).to eq alg
53
+ expect(jwt_payload).to eq payload
54
+ end
55
+
56
+ it 'should display a better error message if payload exp is_a?(Time)' do
57
+ payload['exp'] = Time.now
58
+
59
+ expect do
60
+ JWTB.encode payload, nil, alg
61
+ end.to raise_error JWTB::InvalidPayload
62
+ end
63
+ end
64
+
65
+ %w(HS256 HS512256 HS384 HS512).each do |alg|
66
+ context "alg: #{alg}" do
67
+ it 'should generate a valid token' do
68
+ token = JWTB.encode payload, data[:secret], alg
69
+
70
+ expect(token).to eq data[alg]
71
+ end
72
+
73
+ it 'should decode a valid token' do
74
+ jwt_payload, header = JWTB.decode data[alg], data[:secret], true, algorithm: alg
75
+
76
+ expect(header['alg']).to eq alg
77
+ expect(jwt_payload).to eq payload
78
+ end
79
+
80
+ it 'wrong secret should raise JWTB::DecodeError' do
81
+ expect do
82
+ JWTB.decode data[alg], 'wrong_secret', true, algorithm: alg
83
+ end.to raise_error JWTB::VerificationError
84
+ end
85
+
86
+ it 'wrong secret and verify = false should not raise JWTB::DecodeError' do
87
+ expect do
88
+ JWTB.decode data[alg], 'wrong_secret', false
89
+ end.not_to raise_error
90
+ end
91
+ end
92
+ end
93
+
94
+ %w(RS256 RS384 RS512).each do |alg|
95
+ context "alg: #{alg}" do
96
+ it 'should generate a valid token' do
97
+ token = JWTB.encode payload, data[:rsa_private], alg
98
+
99
+ expect(token).to eq data[alg]
100
+ end
101
+
102
+ it 'should decode a valid token' do
103
+ jwt_payload, header = JWTB.decode data[alg], data[:rsa_public], true, algorithm: alg
104
+
105
+ expect(header['alg']).to eq alg
106
+ expect(jwt_payload).to eq payload
107
+ end
108
+
109
+ it 'wrong key should raise JWTB::DecodeError' do
110
+ key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
111
+
112
+ expect do
113
+ JWTB.decode data[alg], key, true, algorithm: alg
114
+ end.to raise_error JWTB::DecodeError
115
+ end
116
+
117
+ it 'wrong key and verify = false should not raise JWTB::DecodeError' do
118
+ key = OpenSSL::PKey.read File.read(File.join(CERT_PATH, 'rsa-2048-wrong-public.pem'))
119
+
120
+ expect do
121
+ JWTB.decode data[alg], key, false
122
+ end.not_to raise_error
123
+ end
124
+ end
125
+ end
126
+
127
+ %w(ES256 ES384 ES512).each do |alg|
128
+ context "alg: #{alg}" do
129
+ before(:each) do
130
+ data[alg] = JWTB.encode payload, data["#{alg}_private"], alg
131
+ end
132
+
133
+ let(:wrong_key) { OpenSSL::PKey.read(File.read(File.join(CERT_PATH, 'ec256-wrong-public.pem'))) }
134
+
135
+ it 'should generate a valid token' do
136
+ jwt_payload, header = JWTB.decode data[alg], data["#{alg}_public"], true, algorithm: alg
137
+
138
+ expect(header['alg']).to eq alg
139
+ expect(jwt_payload).to eq payload
140
+ end
141
+
142
+ it 'should decode a valid token' do
143
+ jwt_payload, header = JWTB.decode data[alg], data["#{alg}_public"], true, algorithm: alg
144
+
145
+ expect(header['alg']).to eq alg
146
+ expect(jwt_payload).to eq payload
147
+ end
148
+
149
+ it 'wrong key should raise JWTB::DecodeError' do
150
+ expect do
151
+ JWTB.decode data[alg], wrong_key
152
+ end.to raise_error JWTB::DecodeError
153
+ end
154
+
155
+ it 'wrong key and verify = false should not raise JWTB::DecodeError' do
156
+ expect do
157
+ JWTB.decode data[alg], wrong_key, false
158
+ end.not_to raise_error
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'Invalid' do
164
+ it 'algorithm should raise NotImplementedError' do
165
+ expect do
166
+ JWTB.encode payload, 'secret', 'HS255'
167
+ end.to raise_error NotImplementedError
168
+ end
169
+
170
+ it 'ECDSA curve_name should raise JWTB::IncorrectAlgorithm' do
171
+ key = OpenSSL::PKey::EC.new 'secp256k1'
172
+ key.generate_key
173
+
174
+ expect do
175
+ JWTB.encode payload, key, 'ES256'
176
+ end.to raise_error JWTB::IncorrectAlgorithm
177
+
178
+ token = JWTB.encode payload, data['ES256_private'], 'ES256'
179
+ key.private_key = nil
180
+
181
+ expect do
182
+ JWTB.decode token, key
183
+ end.to raise_error JWTB::IncorrectAlgorithm
184
+ end
185
+ end
186
+
187
+ context 'Verify' do
188
+ context 'algorithm' do
189
+ it 'should raise JWTB::IncorrectAlgorithm on missmatch' do
190
+ token = JWTB.encode payload, data[:secret], 'HS512'
191
+
192
+ expect do
193
+ JWTB.decode token, data[:secret], true, algorithm: 'HS384'
194
+ end.to raise_error JWTB::IncorrectAlgorithm
195
+
196
+ expect do
197
+ JWTB.decode token, data[:secret], true, algorithm: 'HS512'
198
+ end.not_to raise_error
199
+ end
200
+
201
+ it 'should raise JWTB::IncorrectAlgorithm if no algorithm is provided' do
202
+ token = JWTB.encode payload, data[:rsa_public].to_s, 'HS256'
203
+
204
+ expect do
205
+ JWTB.decode token, data[:rsa_public], true
206
+ end.to raise_error JWTB::IncorrectAlgorithm
207
+ end
208
+ end
209
+
210
+ context 'issuer claim' do
211
+ let(:iss) { 'ruby-jwt-gem' }
212
+ let(:invalid_token) { JWTB.encode payload, data[:secret] }
213
+
214
+ let :token do
215
+ iss_payload = payload.merge(iss: iss)
216
+ JWTB.encode iss_payload, data[:secret]
217
+ end
218
+
219
+ it 'if verify_iss is set to false (default option) should not raise JWTB::InvalidIssuerError' do
220
+ expect do
221
+ JWTB.decode token, data[:secret], true, iss: iss, algorithm: 'HS256'
222
+ end.not_to raise_error
223
+ end
224
+ end
225
+ end
226
+
227
+ context 'Base64' do
228
+ it 'urlsafe replace + / with - _' do
229
+ allow(Base64).to receive(:encode64) { 'string+with/non+url-safe/characters_' }
230
+ expect(JWTB::Encode.base64url_encode('foo')).to eq('string-with_non-url-safe_characters_')
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,28 @@
1
+ require 'rspec'
2
+ require 'simplecov'
3
+ require 'simplecov-json'
4
+ require 'codeclimate-test-reporter'
5
+ require 'codacy-coverage'
6
+
7
+ Codacy::Reporter.start
8
+
9
+ SimpleCov.configure do
10
+ root File.join(File.dirname(__FILE__), '..')
11
+ project_name 'Ruby JWT - Ruby JSON Web Token implementation'
12
+ add_filter 'spec'
13
+ end
14
+
15
+ SimpleCov.start if ENV['COVERAGE']
16
+
17
+ CERT_PATH = File.join(File.dirname(__FILE__), 'fixtures', 'certs')
18
+
19
+ RSpec.configure do |config|
20
+ config.expect_with :rspec do |c|
21
+ c.syntax = [:should, :expect]
22
+ end
23
+
24
+ config.run_all_when_everything_filtered = true
25
+ config.filter_run :focus
26
+
27
+ config.order = 'random'
28
+ end