Skip to content

Commit 9179675

Browse files
committed
Refactor tests related to notBefore and nbf
This change extracts all tests in the current files related to notBefore and nbf into a single test file. It also adds several missing related tests.
1 parent 677ead6 commit 9179675

6 files changed

+259
-106
lines changed

Diff for: package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "JSON Web Token implementation (symmetric and asymmetric)",
55
"main": "index.js",
66
"scripts": {
7-
"test": "nyc --reporter=html --reporter=text mocha && nsp check && cost-of-modules"
7+
"test:unit": "nyc --reporter=html --reporter=text mocha",
8+
"test": "npm run test:unit && nsp check && cost-of-modules"
89
},
910
"repository": {
1011
"type": "git",

Diff for: test/iat.tests.js

-9
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,4 @@ describe('iat', function () {
1212
expect(result.exp).to.be.closeTo(iat + expiresIn, 0.2);
1313
});
1414

15-
it('should work with a nbf calculated based on numeric iat', function () {
16-
var dateNow = Math.floor(Date.now() / 1000);
17-
var iat = dateNow - 30;
18-
var notBefore = -50;
19-
var token = jwt.sign({foo: 123, iat: iat}, '123', {notBefore: notBefore});
20-
var result = jwt.verify(token, '123');
21-
expect(result.nbf).to.equal(iat + notBefore);
22-
});
23-
2415
});

Diff for: test/jwt.asymmetric_signing.tests.js

-51
Original file line numberDiff line numberDiff line change
@@ -114,57 +114,6 @@ describe('Asymmetric Algorithms', function(){
114114
});
115115
});
116116

117-
describe('when signing a token with not before', function () {
118-
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: -10 * 3600 });
119-
120-
it('should be valid expiration', function (done) {
121-
jwt.verify(token, pub, function (err, decoded) {
122-
assert.isNotNull(decoded);
123-
assert.isNull(err);
124-
done();
125-
});
126-
});
127-
128-
it('should be invalid', function (done) {
129-
// not active token
130-
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: '10m' });
131-
132-
jwt.verify(token, pub, function (err, decoded) {
133-
assert.isUndefined(decoded);
134-
assert.isNotNull(err);
135-
assert.equal(err.name, 'NotBeforeError');
136-
assert.instanceOf(err.date, Date);
137-
assert.instanceOf(err, jwt.NotBeforeError);
138-
done();
139-
});
140-
});
141-
142-
143-
it('should valid when date are equals', function (done) {
144-
var fakeClock = sinon.useFakeTimers({now: 1451908031});
145-
146-
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: 0 });
147-
148-
jwt.verify(token, pub, function (err, decoded) {
149-
fakeClock.uninstall();
150-
assert.isNull(err);
151-
assert.isNotNull(decoded);
152-
done();
153-
});
154-
});
155-
156-
it('should NOT be invalid', function (done) {
157-
// not active token
158-
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: '10m' });
159-
160-
jwt.verify(token, pub, { ignoreNotBefore: true }, function (err, decoded) {
161-
assert.ok(decoded.foo);
162-
assert.equal('bar', decoded.foo);
163-
done();
164-
});
165-
});
166-
});
167-
168117
describe('when signing a token with audience', function () {
169118
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, audience: 'urn:foo' });
170119

Diff for: test/nbf.test.js

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

Diff for: test/schema.tests.js

-18
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,6 @@ describe('schema', function() {
2525
sign({ expiresIn: 10 });
2626
});
2727

28-
it('should validate notBefore', function () {
29-
expect(function () {
30-
sign({ notBefore: '1 monkey' });
31-
}).to.throw(/"notBefore" should be a number of seconds or string representing a timespan/);
32-
expect(function () {
33-
sign({ notBefore: 1.1 });
34-
}).to.throw(/"notBefore" should be a number of seconds or string representing a timespan/);
35-
sign({ notBefore: '10s' });
36-
sign({ notBefore: 10 });
37-
});
38-
3928
it('should validate audience', function () {
4029
expect(function () {
4130
sign({ audience: 10 });
@@ -124,13 +113,6 @@ describe('schema', function() {
124113
sign({ exp: 10.1 });
125114
});
126115

127-
it('should validate nbf', function () {
128-
expect(function () {
129-
sign({ nbf: '1 monkey' });
130-
}).to.throw(/"nbf" should be a number of seconds/);
131-
sign({ nbf: 10.1 });
132-
});
133-
134116
});
135117

136118
});

0 commit comments

Comments
 (0)