Skip to content

Commit ab76ec5

Browse files
committed
Added support for subject and jwt id
1 parent 33b326f commit ab76ec5

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

README.md

+21-1
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ encoded private key for RSA and ECDSA.
2929
* `audience`
3030
* `subject`
3131
* `issuer`
32+
* `jwtid`
33+
* `subject`
3234
* `noTimestamp`
3335
* `headers`
3436

3537
If `payload` is not a buffer or a string, it will be coerced into a string
3638
using `JSON.stringify`.
3739

38-
If any `expiresInMinutes`, `audience`, `subject`, `issuer` are not provided, there is no default. The jwt generated won't include those properties in the payload.
40+
If any `expiresInMinutes`, `audience`, `subject`, `issuer`, `jwtid`, `subject` are not provided, there is no default. The jwt generated won't include those properties in the payload.
3941

4042
Additional headers can be provided via the `headers` object.
4143

@@ -60,6 +62,8 @@ var token = jwt.sign({ foo: 'bar' }, cert, { algorithm: 'RS256'});
6062
* `ignoreExpiration`
6163
* `audience`
6264
* `issuer`
65+
* `jwtid`
66+
* `subject`
6367

6468

6569
(Asynchronous) If a callback is supplied, function acts asynchronously. Callback passed the payload decoded if the signature (and optionally expiration, audience, issuer) are valid. If not, it will be passed the error.
@@ -77,6 +81,8 @@ encoded public key for RSA and ECDSA.
7781
* `audience`: if you want to check audience (`aud`), provide a value here
7882
* `issuer`: if you want to check issuer (`iss`), provide a value here
7983
* `ignoreExpiration`: if `true` do not validate the expiration of the token.
84+
* `jwtid`: if you want to check JWT ID (`jti`), provide a value here
85+
* `subject`: if you want to check subject (`sub`), provide a value here
8086

8187
```js
8288
// verify a token symmetric - synchronous
@@ -119,6 +125,18 @@ jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer' }, function(
119125
// if issuer mismatch, err == invalid issuer
120126
});
121127

128+
// verify jwt id
129+
var cert = fs.readFileSync('public.pem'); // get public key
130+
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid' }, function(err, decoded) {
131+
// if jwt id mismatch, err == invalid jwt id
132+
});
133+
134+
// verify subject
135+
var cert = fs.readFileSync('public.pem'); // get public key
136+
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer', jwtid: 'jwtid', subject: 'subject' }, function(err, decoded) {
137+
// if subject mismatch, err == invalid subject
138+
});
139+
122140
// alg mismatch
123141
var cert = fs.readFileSync('public.pem'); // get public key
124142
jwt.verify(token, cert, { algorithms: ['RS256'] }, function (err, payload) {
@@ -188,6 +206,8 @@ Error object:
188206
* 'invalid signature'
189207
* 'jwt audience invalid. expected: [OPTIONS AUDIENCE]'
190208
* 'jwt issuer invalid. expected: [OPTIONS ISSUER]'
209+
* 'jwt id invalid. expected: [OPTIONS JWT ID]'
210+
* 'jwt subject invalid. expected: [OPTIONS SUBJECT]'
191211

192212
```js
193213
jwt.verify(token, 'shhhhh', function(err, decoded) {

index.js

+13
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ module.exports.sign = function(payload, secretOrPrivateKey, options) {
7171
if (options.subject)
7272
payload.sub = options.subject;
7373

74+
if (options.jwtid)
75+
payload.jti = options.jwtid;
76+
7477
var encoding = 'utf8';
7578
if (options.encoding) {
7679
encoding = options.encoding;
@@ -188,5 +191,15 @@ module.exports.verify = function(jwtString, secretOrPublicKey, options, callback
188191
return done(new JsonWebTokenError('jwt issuer invalid. expected: ' + options.issuer));
189192
}
190193

194+
if (options.subject) {
195+
if (payload.sub !== options.subject)
196+
return done(new JsonWebTokenError('jwt subject invalid. expected: ' + options.subject));
197+
}
198+
199+
if (options.jwtid) {
200+
if (payload.jti !== options.jwtid)
201+
return done(new JsonWebTokenError('jwt id invalid. expected: ' + options.jwtid));
202+
}
203+
191204
return done(null, payload);
192205
};

test/jwt.rs.tests.js

+66
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,72 @@ describe('RS256', function() {
236236
});
237237
});
238238

239+
describe('when signing a token with subject', function() {
240+
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', subject: 'subject' });
241+
242+
it('should check subject', function() {
243+
jwt.verify(token, pub, { subject: 'subject' }, function(err, decoded) {
244+
assert.isNotNull(decoded);
245+
assert.isNull(err);
246+
});
247+
});
248+
249+
it('should throw when invalid subject', function() {
250+
jwt.verify(token, pub, { issuer: 'wrongSubject' }, function(err, decoded) {
251+
assert.isUndefined(decoded);
252+
assert.isNotNull(err);
253+
assert.equal(err.name, 'JsonWebTokenError');
254+
assert.instanceOf(err, jwt.JsonWebTokenError);
255+
});
256+
});
257+
});
258+
259+
describe('when signing a token without subject', function() {
260+
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256' });
261+
262+
it('should check subject', function() {
263+
jwt.verify(token, pub, { subject: 'subject' }, function(err, decoded) {
264+
assert.isUndefined(decoded);
265+
assert.isNotNull(err);
266+
assert.equal(err.name, 'JsonWebTokenError');
267+
assert.instanceOf(err, jwt.JsonWebTokenError);
268+
});
269+
});
270+
});
271+
272+
describe('when signing a token with jwt id', function() {
273+
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', jwtid: 'jwtid' });
274+
275+
it('should check jwt id', function() {
276+
jwt.verify(token, pub, { jwtid: 'jwtid' }, function(err, decoded) {
277+
assert.isNotNull(decoded);
278+
assert.isNull(err);
279+
});
280+
});
281+
282+
it('should throw when invalid jwt id', function() {
283+
jwt.verify(token, pub, { jwtid: 'wrongJwtid' }, function(err, decoded) {
284+
assert.isUndefined(decoded);
285+
assert.isNotNull(err);
286+
assert.equal(err.name, 'JsonWebTokenError');
287+
assert.instanceOf(err, jwt.JsonWebTokenError);
288+
});
289+
});
290+
});
291+
292+
describe('when signing a token without jwt id', function() {
293+
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256' });
294+
295+
it('should check jwt id', function() {
296+
jwt.verify(token, pub, { jwtid: 'jwtid' }, function(err, decoded) {
297+
assert.isUndefined(decoded);
298+
assert.isNotNull(err);
299+
assert.equal(err.name, 'JsonWebTokenError');
300+
assert.instanceOf(err, jwt.JsonWebTokenError);
301+
});
302+
});
303+
});
304+
239305
describe('when verifying a malformed token', function() {
240306
it('should throw', function(done) {
241307
jwt.verify('fruit.fruit.fruit', pub, function(err, decoded) {

0 commit comments

Comments
 (0)