mirror of
https://github.com/hanzoai/node-jsonwebtoken.git
synced 2026-09-24 17:00:48 +00:00
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:
@@ -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
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user