Skip to content

Commit

Permalink
Refactor tests related to expiresIn and exp (auth0#501)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MitMaro authored and ziluvatar committed Jul 6, 2018
1 parent 39adf87 commit 72f0d9e
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 83 deletions.
277 changes: 277 additions & 0 deletions test/exp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
'use strict';

const jwt = require('../');
const expect = require('chai').expect;
const sinon = require('sinon');
const util = require('util');
const testUtils = require('./test-utils');

const base64UrlEncode = testUtils.base64UrlEncode;
const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';

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

describe('expires', function() {
describe('`jwt.sign` "expiresIn" option validation', function () {
[
true,
false,
null,
-1.1,
1.1,
-Infinity,
Infinity,
NaN,
' ',
'invalid',
[],
['foo'],
{},
{foo: 'bar'},
].forEach((expiresIn) => {
it(`should error with with value ${util.inspect(expiresIn)}`, function () {
expect(() => signWithExpiresIn({}, expiresIn)).to.throw(
'"expiresIn" 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(() => signWithExpiresIn({}, '')).to.throw(
'val is not a non-empty string or a valid number. val=""'
);
});

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

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

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

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

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

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

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

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

it('should set correct "exp" with negative number of seconds', function() {
const token = signWithExpiresIn({}, -10);
fakeClock.tick(-10001);

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

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

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

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

fakeClock.tick(-1);

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

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

fakeClock.tick(-10001);

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

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

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

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

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

fakeClock.tick(-1);
const decoded = jwt.decode(token);
const verified = jwt.verify(token, undefined);
expect(decoded).to.deep.equal(verified);
expect(decoded.exp).to.equal(60);
});

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

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

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

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

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

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

it('should set correct "exp" when "iat" is passed', function () {
const token = signWithExpiresIn({iat: 80}, -10);

const decoded = jwt.decode(token);

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

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

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

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

fakeClock.tick(10000);

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

it('should ignore a expired token when "ignoreExpiration" is true', function () {
const token = signWithExpiresIn({}, '-10 s');

const verified = jwt.verify(token, undefined, {ignoreExpiration: true});
expect(verified.iat).to.equal(60);
expect(verified.exp).to.equal(50);
});

it('should error on verify if "exp" is at current time', function() {
const token = signWithExpiresIn({exp: 60});

expect(() => jwt.verify(token, undefined)).to.throw(
jwt.TokenExpiredError,
'jwt expired'
);
});

it('should error on verify if "exp" is before current time using clockTolerance', function () {
const token = signWithExpiresIn({}, -5);

expect(() => jwt.verify(token, undefined, {clockTolerance: 5})).to.throw(
jwt.TokenExpiredError,
'jwt expired'
);
});
});
});
41 changes: 0 additions & 41 deletions test/expires_format.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,10 @@ var expect = require('chai').expect;

describe('expires option', function() {

it('should work with a number of seconds', function () {
var token = jwt.sign({foo: 123}, '123', { expiresIn: 10 });
var result = jwt.verify(token, '123');
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + 10, 0.2);
});

it('should work with a string', function () {
var token = jwt.sign({foo: 123}, '123', { expiresIn: '2d' });
var result = jwt.verify(token, '123');
var two_days_in_secs = 2 * 24 * 60 * 60;
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + two_days_in_secs, 0.2);
});

it('should work with a string second example', function () {
var token = jwt.sign({foo: 123}, '123', { expiresIn: '36h' });
var result = jwt.verify(token, '123');
var day_and_a_half_in_secs = 1.5 * 24 * 60 * 60;
expect(result.exp).to.be.closeTo(Math.floor(Date.now() / 1000) + day_and_a_half_in_secs, 0.2);
});


it('should throw if expires has a bad string format', function () {
expect(function () {
jwt.sign({foo: 123}, '123', { expiresIn: '1 monkey' });
}).to.throw(/"expiresIn" should be a number of seconds or string representing a timespan/);
});

it('should throw if expires is not an string or number', function () {
expect(function () {
jwt.sign({foo: 123}, '123', { expiresIn: { crazy : 213 } });
}).to.throw(/"expiresIn" should be a number of seconds or string representing a timespan/);
});

it('should throw an error if expiresIn and exp are provided', function () {
expect(function () {
jwt.sign({ foo: 123, exp: 839218392183 }, '123', { expiresIn: '5h' });
}).to.throw(/Bad "options.expiresIn" option the payload already has an "exp" property./);
});


it('should throw on deprecated expiresInSeconds option', function () {
expect(function () {
jwt.sign({foo: 123}, '123', { expiresInSeconds: 5 });
}).to.throw('"expiresInSeconds" is not allowed');
});


});
15 changes: 0 additions & 15 deletions test/iat.tests.js

This file was deleted.

12 changes: 3 additions & 9 deletions test/nbf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@ const jwt = require('../');
const expect = require('chai').expect;
const sinon = require('sinon');
const util = require('util');
const testUtils = require('./test-utils');

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

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

const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';

describe('not before', function() {
describe('`jwt.sign` "notBefore" option validation', function () {
[
Expand Down
7 changes: 0 additions & 7 deletions test/non_object_values.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@ describe('non_object_values values', function() {
expect(result).to.equal('hello');
});

//v6 version will throw in this case:
it('should throw with expiresIn', function () {
expect(function () {
jwt.sign('hello', '123', { expiresIn: '12h' });
}).to.throw(/invalid expiresIn option for string payload/);
});

it('should fail to validate audience when the payload is string', function () {
var token = jwt.sign('hello', '123');
expect(function () {
Expand Down
Loading

0 comments on commit 72f0d9e

Please sign in to comment.