Skip to content

Commit

Permalink
Refactor tests related to notBefore and nbf (#497)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MitMaro authored and ziluvatar committed Jun 27, 2018
1 parent fb0084a commit 39adf87
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 107 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
},
"scripts": {
"lint": "eslint .",
"test": "npm run lint && nyc mocha && nsp check && cost-of-modules"
"coverage": "nyc mocha",
"test": "npm run lint && npm run coverage && nsp check && cost-of-modules"
},
"repository": {
"type": "git",
Expand Down
9 changes: 0 additions & 9 deletions test/iat.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,4 @@ describe('iat', function () {
expect(result.exp).to.be.closeTo(iat + expiresIn, 0.2);
});

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

});
52 changes: 0 additions & 52 deletions test/jwt.asymmetric_signing.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ var path = require('path');
var expect = require('chai').expect;
var assert = require('chai').assert;
var ms = require('ms');
var sinon = require('sinon');

function loadKey(filename) {
return fs.readFileSync(path.join(__dirname, filename));
Expand Down Expand Up @@ -114,57 +113,6 @@ describe('Asymmetric Algorithms', function(){
});
});

describe('when signing a token with not before', function () {
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: -10 * 3600 });

it('should be valid expiration', function (done) {
jwt.verify(token, pub, function (err, decoded) {
assert.isNotNull(decoded);
assert.isNull(err);
done();
});
});

it('should be invalid', function (done) {
// not active token
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: '10m' });

jwt.verify(token, pub, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'NotBeforeError');
assert.instanceOf(err.date, Date);
assert.instanceOf(err, jwt.NotBeforeError);
done();
});
});


it('should valid when date are equals', function (done) {
var fakeClock = sinon.useFakeTimers({now: 1451908031});

token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: 0 });

jwt.verify(token, pub, function (err, decoded) {
fakeClock.uninstall();
assert.isNull(err);
assert.isNotNull(decoded);
done();
});
});

it('should NOT be invalid', function (done) {
// not active token
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, notBefore: '10m' });

jwt.verify(token, pub, { ignoreNotBefore: true }, function (err, decoded) {
assert.ok(decoded.foo);
assert.equal('bar', decoded.foo);
done();
});
});
});

describe('when signing a token with audience', function () {
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: algorithm, audience: 'urn:foo' });

Expand Down
281 changes: 281 additions & 0 deletions test/nbf.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
'use strict';

const jwt = require('../');
const expect = require('chai').expect;
const sinon = require('sinon');
const util = require('util');

function base64UrlEncode(str) {
return Buffer.from(str).toString('base64')
.replace(/\=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_")
;
}

function signWithNoBefore(payload, notBefore) {
const options = {algorithm: 'none'};
if (notBefore !== undefined) {
options.notBefore = notBefore;
}
return jwt.sign(payload, undefined, options);
}

const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';

describe('not before', function() {
describe('`jwt.sign` "notBefore" option validation', function () {
[
true,
false,
null,
-1.1,
1.1,
-Infinity,
Infinity,
NaN,
' ',
'invalid',
[],
['foo'],
{},
{foo: 'bar'},
].forEach((notBefore) => {
it(`should error with with value ${util.inspect(notBefore)}`, function () {
expect(() => signWithNoBefore({}, notBefore)).to.throw(
'"notBefore" should be a number of seconds or string representing a timespan'
);
});
});

// TODO this should throw the same error as other invalid inputs
it(`should error with with value ''`, function () {
expect(() => signWithNoBefore({}, '')).to.throw(
'val is not a non-empty string or a valid number. val=""'
);
});

// undefined needs special treatment because {} is not the same as {notBefore: undefined}
it('should error with with value undefined', function () {
expect(() => jwt.sign({}, undefined, {notBefore: undefined, algorithm: 'none'})).to.throw(
'"notBefore" should be a number of seconds or string representing a timespan'
);
});

it('should error when "nbf" is in payload', function () {
expect(() => signWithNoBefore({nbf: 100}, 100)).to.throw(
'Bad "options.notBefore" option the payload already has an "nbf" property.'
);
});

it('should error with a string payload', function () {
expect(() => signWithNoBefore('a string payload', 100)).to.throw(
'invalid notBefore option for string payload'
);
});

it('should error with a Buffer payload', function () {
expect(() => signWithNoBefore(new Buffer('a Buffer payload'), 100)).to.throw(
'invalid notBefore option for object payload'
);
});
});

describe('`jwt.sign` "nbf" claim validation', function () {
[
true,
false,
null,
undefined,
'',
' ',
'invalid',
[],
['foo'],
{},
{foo: 'bar'},
].forEach((nbf) => {
it(`should error with with value ${util.inspect(nbf)}`, function () {
expect(() => signWithNoBefore({nbf})).to.throw(
'"nbf" should be a number of seconds'
);
});
});
});

describe('"nbf" in payload validation', function () {
[
true,
false,
null,
-Infinity,
Infinity,
NaN,
'',
' ',
'invalid',
[],
['foo'],
{},
{foo: 'bar'},
].forEach((nbf) => {
it(`should error with with value ${util.inspect(nbf)}`, function () {
const encodedPayload = base64UrlEncode(JSON.stringify({nbf}));
const token = `${noneAlgorithmHeader}.${encodedPayload}.`;
expect(() => jwt.verify(token, undefined)).to.throw(
jwt.JsonWebTokenError,
'invalid nbf value'
);
});
})
});

describe('when signing and verifying a token with "notBefore" option', function () {
let fakeClock;
beforeEach(function () {
fakeClock = sinon.useFakeTimers({now: 60000});
});

afterEach(function () {
fakeClock.uninstall();
});

it('should set correct "nbf" with negative number of seconds', function () {
const token = signWithNoBefore({}, -10);
const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(50);
});

it('should set correct "nbf" with positive number of seconds', function () {
const token = signWithNoBefore({}, 10);

fakeClock.tick(10000);
const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(70);
});

it('should set correct "nbf" with zero seconds', function () {
const token = signWithNoBefore({}, 0);

const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(60);
});

it('should set correct "nbf" with negative string timespan', function () {
const token = signWithNoBefore({}, '-10 s');

const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(50);
});


it('should set correct "nbf" with positive string timespan', function () {
const token = signWithNoBefore({}, '10 s');

fakeClock.tick(10000);
const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(70);
});

it('should set correct "nbf" with zero string timespan', function () {
const token = signWithNoBefore({}, '0 s');

const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(60);
});

// TODO an nbf of -Infinity should fail validation
it('should set null "nbf" when given -Infinity', function () {
const token = signWithNoBefore({nbf: -Infinity});

const decoded = jwt.decode(token);
expect(decoded.nbf).to.be.null;
});

// TODO an nbf of Infinity should fail validation
it('should set null "nbf" when given value Infinity', function () {
const token = signWithNoBefore({nbf: Infinity});

const decoded = jwt.decode(token);
expect(decoded.nbf).to.be.null;
});

// TODO an nbf of NaN should fail validation
it('should set null "nbf" when given value NaN', function () {
const token = signWithNoBefore({nbf: NaN});

const decoded = jwt.decode(token);
expect(decoded.nbf).to.be.null;
});

it('should set correct "nbf" when "iat" is passed', function () {
const token = signWithNoBefore({iat: 40}, -10);

const decoded = jwt.decode(token);

const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.nbf).to.equal(30);
});

it('should verify "nbf" using "clockTimestamp"', function () {
const token = signWithNoBefore({}, 10);

const verified = jwt.verify(token, undefined, {clockTimestamp: 70});
expect(verified.iat).to.equal(60);
expect(verified.nbf).to.equal(70);
});

it('should verify "nbf" using "clockTolerance"', function () {
const token = signWithNoBefore({}, 5);

const verified = jwt.verify(token, undefined, {clockTolerance: 6});
expect(verified.iat).to.equal(60);
expect(verified.nbf).to.equal(65);
});

it('should ignore a not active token when "ignoreNotBefore" is true', function () {
const token = signWithNoBefore({}, '10 s');

const verified = jwt.verify(token, undefined, {ignoreNotBefore: true});
expect(verified.iat).to.equal(60);
expect(verified.nbf).to.equal(70);
});

it('should error on verify if "nbf" is after current time', function () {
const token = signWithNoBefore({nbf: 61});

expect(() => jwt.verify(token, undefined)).to.throw(
jwt.NotBeforeError,
'jwt not active'
);
});

it('should error on verify if "nbf" is after current time using clockTolerance', function () {
const token = signWithNoBefore({}, 5);

expect(() => jwt.verify(token, undefined, {clockTolerance: 4})).to.throw(
jwt.NotBeforeError,
'jwt not active'
);
});
});
});
Loading

0 comments on commit 39adf87

Please sign in to comment.