Skip to content

Commit 253ceaf

Browse files
committed
Merge branch 'jden-max-age'
2 parents 8053c17 + 8e89ee4 commit 253ceaf

File tree

3 files changed

+120
-4
lines changed

3 files changed

+120
-4
lines changed

index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var JWT = module.exports;
44

55
var JsonWebTokenError = JWT.JsonWebTokenError = require('./lib/JsonWebTokenError');
66
var TokenExpiredError = JWT.TokenExpiredError = require('./lib/TokenExpiredError');
7-
7+
var ms = require('ms')
88

99
JWT.decode = function (jwt, options) {
1010
options = options || {};
@@ -202,5 +202,15 @@ JWT.verify = function(jwtString, secretOrPublicKey, options, callback) {
202202
return done(new JsonWebTokenError('jwt issuer invalid. expected: ' + options.issuer));
203203
}
204204

205+
if (options.maxAge) {
206+
var maxAge = ms(options.maxAge);
207+
if (typeof payload.iat !== 'number') {
208+
return done(new JsonWebTokenError('iat required when maxAge is specified'));
209+
}
210+
if (Date.now() - (payload.iat * 1000) > maxAge) {
211+
return done(new TokenExpiredError('maxAge exceeded', new Date(payload.iat * 1000 + maxAge)));
212+
}
213+
}
214+
205215
return done(null, payload);
206216
};

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
"url": "https://github.com/auth0/node-jsonwebtoken/issues"
2020
},
2121
"dependencies": {
22-
"jws": "^3.0.0"
22+
"jws": "^3.0.0",
23+
"ms": "^0.7.1"
2324
},
2425
"devDependencies": {
2526
"atob": "^1.1.2",
2627
"chai": "^1.10.0",
27-
"mocha": "^2.1.0"
28+
"mocha": "^2.1.0",
29+
"sinon": "^1.15.4"
2830
},
2931
"engines": {
3032
"npm": ">=1.4.28"

test/verify.tests.js

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ var jwt = require('../index');
22
var jws = require('jws');
33
var fs = require('fs');
44
var path = require('path');
5+
var sinon = require('sinon');
56

67
var assert = require('chai').assert;
78

89
describe('verify', function() {
910
var pub = fs.readFileSync(path.join(__dirname, 'pub.pem'));
1011
var priv = fs.readFileSync(path.join(__dirname, 'priv.pem'));
1112

12-
it('should first assume JSON claim set', function () {
13+
it('should first assume JSON claim set', function (done) {
1314
var header = { alg: 'RS256' };
1415
var payload = { iat: Math.floor(Date.now() / 1000 ) };
1516

@@ -23,6 +24,109 @@ describe('verify', function() {
2324
jwt.verify(signed, pub, {typ: 'JWT'}, function(err, p) {
2425
assert.isNull(err);
2526
assert.deepEqual(p, payload);
27+
done();
2628
});
2729
});
30+
31+
describe('expiration', function () {
32+
// { foo: 'bar', iat: 1437018582, exp: 1437018583 }
33+
var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE0MzcwMTg1ODIsImV4cCI6MTQzNzAxODU4M30.NmMv7sXjM1dW0eALNXud8LoXknZ0mH14GtnFclwJv0s';
34+
var key = 'key';
35+
36+
var clock;
37+
afterEach(function () {
38+
try { clock.restore(); } catch (e) {}
39+
});
40+
41+
it('should error on expired token', function (done) {
42+
clock = sinon.useFakeTimers(1437018650000);
43+
var options = {algorithms: ['HS256']};
44+
45+
jwt.verify(token, key, options, function (err, p) {
46+
assert.equal(err.name, 'TokenExpiredError');
47+
assert.equal(err.message, 'jwt expired');
48+
assert.equal(err.expiredAt.constructor.name, 'Date');
49+
assert.equal(Number(err.expiredAt), 1437018583000);
50+
assert.isUndefined(p);
51+
done();
52+
});
53+
});
54+
55+
it('should not error on unexpired token', function (done) {
56+
clock = sinon.useFakeTimers(1437018582000);
57+
var options = {algorithms: ['HS256']}
58+
59+
jwt.verify(token, key, options, function (err, p) {
60+
assert.isNull(err);
61+
assert.equal(p.foo, 'bar');
62+
done();
63+
});
64+
});
65+
66+
describe('option: maxAge', function () {
67+
it('should error for claims issued before a certain timespan', function (done) {
68+
clock = sinon.useFakeTimers(1437018582500);
69+
var options = {algorithms: ['HS256'], maxAge: '321ms'};
70+
71+
jwt.verify(token, key, options, function (err, p) {
72+
assert.equal(err.name, 'TokenExpiredError');
73+
assert.equal(err.message, 'maxAge exceeded');
74+
assert.equal(err.expiredAt.constructor.name, 'Date');
75+
assert.equal(Number(err.expiredAt), 1437018582321);
76+
assert.isUndefined(p);
77+
done();
78+
});
79+
});
80+
it('should not error if within maxAge timespan', function (done) {
81+
clock = sinon.useFakeTimers(1437018582500);
82+
var options = {algorithms: ['HS256'], maxAge: '600ms'};
83+
84+
jwt.verify(token, key, options, function (err, p) {
85+
assert.isNull(err);
86+
assert.equal(p.foo, 'bar');
87+
done();
88+
});
89+
});
90+
it('can be more restrictive than expiration', function (done) {
91+
clock = sinon.useFakeTimers(1437018582900);
92+
var options = {algorithms: ['HS256'], maxAge: '800ms'};
93+
94+
jwt.verify(token, key, options, function (err, p) {
95+
assert.equal(err.name, 'TokenExpiredError');
96+
assert.equal(err.message, 'maxAge exceeded');
97+
assert.equal(err.expiredAt.constructor.name, 'Date');
98+
assert.equal(Number(err.expiredAt), 1437018582800);
99+
assert.isUndefined(p);
100+
done();
101+
});
102+
});
103+
it('cannot be more permissive than expiration', function (done) {
104+
clock = sinon.useFakeTimers(1437018583100);
105+
var options = {algorithms: ['HS256'], maxAge: '1200ms'};
106+
107+
jwt.verify(token, key, options, function (err, p) {
108+
// maxAge not exceded, but still expired
109+
assert.equal(err.name, 'TokenExpiredError');
110+
assert.equal(err.message, 'jwt expired');
111+
assert.equal(err.expiredAt.constructor.name, 'Date');
112+
assert.equal(Number(err.expiredAt), 1437018583000);
113+
assert.isUndefined(p);
114+
done();
115+
});
116+
});
117+
it('should error if maxAge is specified but there is no iat claim', function (done) {
118+
clock = sinon.useFakeTimers(1437018582900);
119+
var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIifQ.0MBPd4Bru9-fK_HY3xmuDAc6N_embknmNuhdb9bKL_U';
120+
var options = {algorithms: ['HS256'], maxAge: '1s'};
121+
122+
jwt.verify(token, key, options, function (err, p) {
123+
assert.equal(err.name, 'JsonWebTokenError');
124+
assert.equal(err.message, 'iat required when maxAge is specified');
125+
assert.isUndefined(p);
126+
done();
127+
});
128+
});
129+
});
130+
});
131+
28132
});

0 commit comments

Comments
 (0)