Implement async/sync tests for exp claim (#536)

Refactor existing tests for the exp claim to use the async/sync test
helpers. This required fixing a case where an error was improperly
handled in the async case.
This commit is contained in:
Tim Oram
2018-10-16 18:57:20 +02:00
committed by Eduardo Diaz
parent 86a40d8924
commit da8a850628
2 changed files with 224 additions and 143 deletions
+6 -1
View File
@@ -154,7 +154,12 @@ module.exports = function (payload, secretOrPrivateKey, options, callback) {
}
if (typeof options.expiresIn !== 'undefined' && typeof payload === 'object') {
payload.exp = timespan(options.expiresIn, timestamp);
try {
payload.exp = timespan(options.expiresIn, timestamp);
}
catch (err) {
return failure(err);
}
if (typeof payload.exp === 'undefined') {
return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
}
+218 -142
View File
@@ -9,12 +9,12 @@ const testUtils = require('./test-utils');
const base64UrlEncode = testUtils.base64UrlEncode;
const noneAlgorithmHeader = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0';
function signWithExpiresIn(payload, expiresIn) {
function signWithExpiresIn(expiresIn, payload, callback) {
const options = {algorithm: 'none'};
if (expiresIn !== undefined) {
options.expiresIn = expiresIn;
}
return jwt.sign(payload, undefined, options);
testUtils.signJWTHelper(payload, 'secret', options, callback);
}
describe('expires', function() {
@@ -35,43 +35,68 @@ describe('expires', function() {
{},
{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'
);
it(`should error with with value ${util.inspect(expiresIn)}`, function (done) {
signWithExpiresIn(expiresIn, {}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property('message')
.match(/"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=""'
);
it(`should error with with value ''`, function (done) {
signWithExpiresIn('', {}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property('message', '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 with with value undefined', function (done) {
testUtils.signJWTHelper({}, undefined, {expiresIn: undefined, algorithm: 'none'}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property(
'message',
'"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 when "exp" is in payload', function(done) {
signWithExpiresIn(100, {exp: 100}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property(
'message',
'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 string payload', function(done) {
signWithExpiresIn(100, 'a string payload', (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property('message', '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'
);
it('should error with a Buffer payload', function(done) {
signWithExpiresIn(100, Buffer.from('a Buffer payload'), (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property('message', 'invalid expiresIn option for object payload');
});
});
});
});
@@ -89,10 +114,13 @@ describe('expires', function() {
{},
{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'
);
it(`should error with with value ${util.inspect(exp)}`, function (done) {
signWithExpiresIn(undefined, {exp}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(Error);
expect(err).to.have.property('message', '"exp" should be a number of seconds');
});
});
});
});
});
@@ -113,13 +141,15 @@ describe('expires', function() {
{},
{foo: 'bar'},
].forEach((exp) => {
it(`should error with with value ${util.inspect(exp)}`, function () {
it(`should error with with value ${util.inspect(exp)}`, function (done) {
const encodedPayload = base64UrlEncode(JSON.stringify({exp}));
const token = `${noneAlgorithmHeader}.${encodedPayload}.`;
expect(() => jwt.verify(token, undefined)).to.throw(
jwt.JsonWebTokenError,
'invalid exp value'
);
testUtils.verifyJWTHelper(token, undefined, {exp}, (err) => {
testUtils.asyncCheck(done, () => {
expect(err).to.be.instanceOf(jwt.JsonWebTokenError);
expect(err).to.have.property('message', 'invalid exp value');
});
});
});
})
});
@@ -134,144 +164,190 @@ describe('expires', 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 negative number of seconds', function(done) {
signWithExpiresIn(-10, {}, (e1, token) => {
fakeClock.tick(-10001);
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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 positive number of seconds', function(done) {
signWithExpiresIn(10, {}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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 zero seconds', function(done) {
signWithExpiresIn(0, {}, (e1, token) => {
fakeClock.tick(-1);
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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 negative string timespan', function(done) {
signWithExpiresIn('-10 s', {}, (e1, token) => {
fakeClock.tick(-10001);
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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 positive string timespan', function(done) {
signWithExpiresIn('10 s', {}, (e1, token) => {
fakeClock.tick(-10001);
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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);
it('should set correct "exp" with zero string timespan', function(done) {
signWithExpiresIn('0 s', {}, (e1, token) => {
fakeClock.tick(-1);
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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;
it('should set null "exp" when given -Infinity', function (done) {
signWithExpiresIn(undefined, {exp: -Infinity}, (err, token) => {
const decoded = jwt.decode(token);
testUtils.asyncCheck(done, () => {
expect(err).to.be.null;
expect(decoded).to.have.property('exp', 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;
it('should set null "exp" when given value Infinity', function (done) {
signWithExpiresIn(undefined, {exp: Infinity}, (err, token) => {
const decoded = jwt.decode(token);
testUtils.asyncCheck(done, () => {
expect(err).to.be.null;
expect(decoded).to.have.property('exp', 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 null "exp" when given value NaN', function (done) {
signWithExpiresIn(undefined, {exp: NaN}, (err, token) => {
const decoded = jwt.decode(token);
testUtils.asyncCheck(done, () => {
expect(err).to.be.null;
expect(decoded).to.have.property('exp', 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 set correct "exp" when "iat" is passed', function (done) {
signWithExpiresIn(-10, {iat: 80}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('exp', 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 "clockTimestamp"', function (done) {
signWithExpiresIn(10, {}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {clockTimestamp: 69}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('iat', 60);
expect(decoded).to.have.property('exp', 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 verify "exp" using "clockTolerance"', function (done) {
signWithExpiresIn(5, {}, (e1, token) => {
fakeClock.tick(10000);
testUtils.verifyJWTHelper(token, undefined, {clockTimestamp: 6}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('iat', 60);
expect(decoded).to.have.property('exp', 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 ignore a expired token when "ignoreExpiration" is true', function (done) {
signWithExpiresIn('-10 s', {}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {ignoreExpiration: true}, (e2, decoded) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.null;
expect(decoded).to.have.property('iat', 60);
expect(decoded).to.have.property('exp', 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 at current time', function(done) {
signWithExpiresIn(undefined, {exp: 60}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {}, (e2) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.instanceOf(jwt.TokenExpiredError);
expect(e2).to.have.property('message', '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'
);
it('should error on verify if "exp" is before current time using clockTolerance', function (done) {
signWithExpiresIn(-5, {}, (e1, token) => {
testUtils.verifyJWTHelper(token, undefined, {clockTolerance: 5}, (e2) => {
testUtils.asyncCheck(done, () => {
expect(e1).to.be.null;
expect(e2).to.be.instanceOf(jwt.TokenExpiredError);
expect(e2).to.have.property('message', 'jwt expired');
});
});
});
});
});
});