Skip to content

Refactor tests related to expiresIn and exp #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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