Skip to content

Commit 72f0d9e

Browse files
MitMaroziluvatar
authored andcommitted
Refactor tests related to expiresIn and exp (#501)
This change extracts all tests in the current test files related to expiresIn and exp into a single test file. It also adds several missing tests.
1 parent 39adf87 commit 72f0d9e

7 files changed

+293
-83
lines changed

test/exp.test.js

+277
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
'use strict';
2+
3+
const jwt = require('../');
4+
const expect = require('chai').expect;
5+
const sinon = require('sinon');
6+
const util = require('util');
7+
const testUtils = require('./test-utils');
8+
9+
const base64UrlEncode = testUtils.base64UrlEncode;
10+
const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';
11+
12+
function signWithExpiresIn(payload, expiresIn) {
13+
const options = {algorithm: 'none'};
14+
if (expiresIn !== undefined) {
15+
options.expiresIn = expiresIn;
16+
}
17+
return jwt.sign(payload, undefined, options);
18+
}
19+
20+
describe('expires', function() {
21+
describe('`jwt.sign` "expiresIn" option validation', function () {
22+
[
23+
true,
24+
false,
25+
null,
26+
-1.1,
27+
1.1,
28+
-Infinity,
29+
Infinity,
30+
NaN,
31+
' ',
32+
'invalid',
33+
[],
34+
['foo'],
35+
{},
36+
{foo: 'bar'},
37+
].forEach((expiresIn) => {
38+
it(`should error with with value ${util.inspect(expiresIn)}`, function () {
39+
expect(() => signWithExpiresIn({}, expiresIn)).to.throw(
40+
'"expiresIn" should be a number of seconds or string representing a timespan'
41+
);
42+
});
43+
});
44+
45+
// TODO this should throw the same error as other invalid inputs
46+
it(`should error with with value ''`, function () {
47+
expect(() => signWithExpiresIn({}, '')).to.throw(
48+
'val is not a non-empty string or a valid number. val=""'
49+
);
50+
});
51+
52+
// undefined needs special treatment because {} is not the same as {expiresIn: undefined}
53+
it('should error with with value undefined', function () {
54+
expect(() =>jwt.sign({}, undefined, {expiresIn: undefined, algorithm: 'none'})).to.throw(
55+
'"expiresIn" should be a number of seconds or string representing a timespan'
56+
);
57+
});
58+
59+
it ('should error when "exp" is in payload', function() {
60+
expect(() => signWithExpiresIn({exp: 100}, 100)).to.throw(
61+
'Bad "options.expiresIn" option the payload already has an "exp" property.'
62+
);
63+
});
64+
65+
it('should error with a string payload', function() {
66+
expect(() => signWithExpiresIn('a string payload', 100)).to.throw(
67+
'invalid expiresIn option for string payload'
68+
);
69+
});
70+
71+
it('should error with a Buffer payload', function() {
72+
expect(() => signWithExpiresIn(Buffer.from('a Buffer payload'), 100)).to.throw(
73+
'invalid expiresIn option for object payload'
74+
);
75+
});
76+
});
77+
78+
describe('`jwt.sign` "exp" claim validation', function () {
79+
[
80+
true,
81+
false,
82+
null,
83+
undefined,
84+
'',
85+
' ',
86+
'invalid',
87+
[],
88+
['foo'],
89+
{},
90+
{foo: 'bar'},
91+
].forEach((exp) => {
92+
it(`should error with with value ${util.inspect(exp)}`, function () {
93+
expect(() => signWithExpiresIn({exp})).to.throw(
94+
'"exp" should be a number of seconds'
95+
);
96+
});
97+
});
98+
});
99+
100+
describe('"exp" in payload validation', function () {
101+
[
102+
true,
103+
false,
104+
null,
105+
-Infinity,
106+
Infinity,
107+
NaN,
108+
'',
109+
' ',
110+
'invalid',
111+
[],
112+
['foo'],
113+
{},
114+
{foo: 'bar'},
115+
].forEach((exp) => {
116+
it(`should error with with value ${util.inspect(exp)}`, function () {
117+
const encodedPayload = base64UrlEncode(JSON.stringify({exp}));
118+
const token = `${noneAlgorithmHeader}.${encodedPayload}.`;
119+
expect(() => jwt.verify(token, undefined)).to.throw(
120+
jwt.JsonWebTokenError,
121+
'invalid exp value'
122+
);
123+
});
124+
})
125+
});
126+
127+
describe('when signing and verifying a token with expires option', function () {
128+
let fakeClock;
129+
beforeEach(function() {
130+
fakeClock = sinon.useFakeTimers({now: 60000});
131+
});
132+
133+
afterEach(function() {
134+
fakeClock.uninstall();
135+
});
136+
137+
it('should set correct "exp" with negative number of seconds', function() {
138+
const token = signWithExpiresIn({}, -10);
139+
fakeClock.tick(-10001);
140+
141+
const decoded = jwt.decode(token);
142+
const verified = jwt.verify(token, undefined);
143+
expect(decoded).to.deep.equal(verified);
144+
expect(decoded.exp).to.equal(50);
145+
});
146+
147+
it('should set correct "exp" with positive number of seconds', function() {
148+
const token = signWithExpiresIn({}, 10);
149+
150+
const decoded = jwt.decode(token);
151+
const verified = jwt.verify(token, undefined);
152+
expect(decoded).to.deep.equal(verified);
153+
expect(decoded.exp).to.equal(70);
154+
});
155+
156+
it('should set correct "exp" with zero seconds', function() {
157+
const token = signWithExpiresIn({}, 0);
158+
159+
fakeClock.tick(-1);
160+
161+
const decoded = jwt.decode(token);
162+
const verified = jwt.verify(token, undefined);
163+
expect(decoded).to.deep.equal(verified);
164+
expect(decoded.exp).to.equal(60);
165+
});
166+
167+
it('should set correct "exp" with negative string timespan', function() {
168+
const token = signWithExpiresIn({}, '-10 s');
169+
170+
fakeClock.tick(-10001);
171+
172+
const decoded = jwt.decode(token);
173+
const verified = jwt.verify(token, undefined);
174+
expect(decoded).to.deep.equal(verified);
175+
expect(decoded.exp).to.equal(50);
176+
});
177+
178+
it('should set correct "exp" with positive string timespan', function() {
179+
const token = signWithExpiresIn({}, '10 s');
180+
181+
fakeClock.tick(-10001);
182+
const decoded = jwt.decode(token);
183+
184+
const verified = jwt.verify(token, undefined);
185+
expect(decoded).to.deep.equal(verified);
186+
expect(decoded.exp).to.equal(70);
187+
});
188+
189+
it('should set correct "exp" with zero string timespan', function() {
190+
const token = signWithExpiresIn({}, '0 s');
191+
192+
fakeClock.tick(-1);
193+
const decoded = jwt.decode(token);
194+
const verified = jwt.verify(token, undefined);
195+
expect(decoded).to.deep.equal(verified);
196+
expect(decoded.exp).to.equal(60);
197+
});
198+
199+
// TODO an exp of -Infinity should fail validation
200+
it('should set null "exp" when given -Infinity', function () {
201+
const token = signWithExpiresIn({exp: -Infinity});
202+
203+
const decoded = jwt.decode(token);
204+
expect(decoded.exp).to.be.null;
205+
});
206+
207+
// TODO an exp of Infinity should fail validation
208+
it('should set null "exp" when given value Infinity', function () {
209+
const token = signWithExpiresIn({exp: Infinity});
210+
211+
const decoded = jwt.decode(token);
212+
expect(decoded.exp).to.be.null;
213+
});
214+
215+
// TODO an exp of NaN should fail validation
216+
it('should set null "exp" when given value NaN', function () {
217+
const token = signWithExpiresIn({exp: NaN});
218+
219+
const decoded = jwt.decode(token);
220+
expect(decoded.exp).to.be.null;
221+
});
222+
223+
it('should set correct "exp" when "iat" is passed', function () {
224+
const token = signWithExpiresIn({iat: 80}, -10);
225+
226+
const decoded = jwt.decode(token);
227+
228+
const verified = jwt.verify(token, undefined);
229+
expect(decoded).to.deep.equal(verified);
230+
expect(decoded.exp).to.equal(70);
231+
});
232+
233+
it('should verify "exp" using "clockTimestamp"', function () {
234+
const token = signWithExpiresIn({}, 10);
235+
236+
const verified = jwt.verify(token, undefined, {clockTimestamp: 69});
237+
expect(verified.iat).to.equal(60);
238+
expect(verified.exp).to.equal(70);
239+
});
240+
241+
it('should verify "exp" using "clockTolerance"', function () {
242+
const token = signWithExpiresIn({}, 5);
243+
244+
fakeClock.tick(10000);
245+
246+
const verified = jwt.verify(token, undefined, {clockTolerance: 6});
247+
expect(verified.iat).to.equal(60);
248+
expect(verified.exp).to.equal(65);
249+
});
250+
251+
it('should ignore a expired token when "ignoreExpiration" is true', function () {
252+
const token = signWithExpiresIn({}, '-10 s');
253+
254+
const verified = jwt.verify(token, undefined, {ignoreExpiration: true});
255+
expect(verified.iat).to.equal(60);
256+
expect(verified.exp).to.equal(50);
257+
});
258+
259+
it('should error on verify if "exp" is at current time', function() {
260+
const token = signWithExpiresIn({exp: 60});
261+
262+
expect(() => jwt.verify(token, undefined)).to.throw(
263+
jwt.TokenExpiredError,
264+
'jwt expired'
265+
);
266+
});
267+
268+
it('should error on verify if "exp" is before current time using clockTolerance', function () {
269+
const token = signWithExpiresIn({}, -5);
270+
271+
expect(() => jwt.verify(token, undefined, {clockTolerance: 5})).to.throw(
272+
jwt.TokenExpiredError,
273+
'jwt expired'
274+
);
275+
});
276+
});
277+
});

test/expires_format.tests.js

-41
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,10 @@ var expect = require('chai').expect;
33

44
describe('expires option', function() {
55

6-
it('should work with a number of seconds', function () {
7-
var token = jwt.sign({foo: 123}, '123', { expiresIn: 10 });
8-
var result = jwt.verify(token, '123');
9-
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + 10, 0.2);
10-
});
11-
12-
it('should work with a string', function () {
13-
var token = jwt.sign({foo: 123}, '123', { expiresIn: '2d' });
14-
var result = jwt.verify(token, '123');
15-
var two_days_in_secs = 2 * 24 * 60 * 60;
16-
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + two_days_in_secs, 0.2);
17-
});
18-
19-
it('should work with a string second example', function () {
20-
var token = jwt.sign({foo: 123}, '123', { expiresIn: '36h' });
21-
var result = jwt.verify(token, '123');
22-
var day_and_a_half_in_secs = 1.5 * 24 * 60 * 60;
23-
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + day_and_a_half_in_secs, 0.2);
24-
});
25-
26-
27-
it('should throw if expires has a bad string format', function () {
28-
expect(function () {
29-
jwt.sign({foo: 123}, '123', { expiresIn: '1 monkey' });
30-
}).to.throw(/"expiresIn" should be a number of seconds or string representing a timespan/);
31-
});
32-
33-
it('should throw if expires is not an string or number', function () {
34-
expect(function () {
35-
jwt.sign({foo: 123}, '123', { expiresIn: { crazy : 213 } });
36-
}).to.throw(/"expiresIn" should be a number of seconds or string representing a timespan/);
37-
});
38-
39-
it('should throw an error if expiresIn and exp are provided', function () {
40-
expect(function () {
41-
jwt.sign({ foo: 123, exp: 839218392183 }, '123', { expiresIn: '5h' });
42-
}).to.throw(/Bad "options.expiresIn" option the payload already has an "exp" property./);
43-
});
44-
45-
466
it('should throw on deprecated expiresInSeconds option', function () {
477
expect(function () {
488
jwt.sign({foo: 123}, '123', { expiresInSeconds: 5 });
499
}).to.throw('"expiresInSeconds" is not allowed');
5010
});
5111

52-
5312
});

test/iat.tests.js

-15
This file was deleted.

test/nbf.test.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,10 @@ const jwt = require('../');
44
const expect = require('chai').expect;
55
const sinon = require('sinon');
66
const util = require('util');
7+
const testUtils = require('./test-utils');
78

8-
function base64UrlEncode(str) {
9-
return Buffer.from(str).toString('base64')
10-
.replace(/\=/g, "")
11-
.replace(/\+/g, "-")
12-
.replace(/\//g, "_")
13-
;
14-
}
9+
const base64UrlEncode = testUtils.base64UrlEncode;
10+
const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';
1511

1612
function signWithNoBefore(payload, notBefore) {
1713
const options = {algorithm: 'none'};
@@ -21,8 +17,6 @@ function signWithNoBefore(payload, notBefore) {
2117
return jwt.sign(payload, undefined, options);
2218
}
2319

24-
const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';
25-
2620
describe('not before', function() {
2721
describe('`jwt.sign` "notBefore" option validation', function () {
2822
[

test/non_object_values.tests.js

-7
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ describe('non_object_values values', function() {
1010
expect(result).to.equal('hello');
1111
});
1212

13-
//v6 version will throw in this case:
14-
it('should throw with expiresIn', function () {
15-
expect(function () {
16-
jwt.sign('hello', '123', { expiresIn: '12h' });
17-
}).to.throw(/invalid expiresIn option for string payload/);
18-
});
19-
2013
it('should fail to validate audience when the payload is string', function () {
2114
var token = jwt.sign('hello', '123');
2215
expect(function () {

0 commit comments

Comments
 (0)