Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: auth0/node-jsonwebtoken
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v8.0.1
Choose a base ref
...
head repository: auth0/node-jsonwebtoken
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v8.1.0
Choose a head ref
  • 7 commits
  • 7 files changed
  • 4 contributors

Commits on Sep 15, 2017

  1. Fix typo in 'options.header' reference; Update Buffer() example to us…

    …e recommended method (#380)
    oughter authored and ziluvatar committed Sep 15, 2017
    Copy the full SHA
    128a9e1 View commit details

Commits on Oct 9, 2017

  1. #385: Tweak README (#408)

    * #385: Tweak README
    
    * #385 Further wording tweaks for consistency.
    aldermoovel authored and ziluvatar committed Oct 9, 2017
    Copy the full SHA
    d3f996b View commit details
  2. Copy the full SHA
    77ee965 View commit details
  3. Enhance audience check to verify against regular expressions (#398)

    * Enhance audience check to verify against regular expressions
    
    * Enhance audience check to verify against regular expressions
    
    * Adapted README to have a showcase of the new RegExp-check for the audience validation
    TheBusCantSwim authored and ziluvatar committed Oct 9, 2017
    Copy the full SHA
    81501a1 View commit details
  4. #403: Clarify error wording. (#409)

    * #403: Clarify error wording.
    
    * #403: Improve wording for payload vs options
    aldermoovel authored and ziluvatar committed Oct 9, 2017
    Copy the full SHA
    bb27eb3 View commit details
  5. update changelog

    ziluvatar committed Oct 9, 2017
    Copy the full SHA
    5c08f65 View commit details
  6. 8.1.0

    ziluvatar committed Oct 9, 2017
    Copy the full SHA
    efa517a View commit details
Showing with 113 additions and 20 deletions.
  1. +8 −1 CHANGELOG.md
  2. +6 −6 README.md
  3. +4 −2 lib/JsonWebTokenError.js
  4. +1 −1 package.json
  5. +15 −7 sign.js
  6. +74 −2 test/jwt.asymmetric_signing.tests.js
  7. +5 −1 verify.js
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,12 +4,19 @@
All notable changes to this project will be documented in this file starting from version **v4.0.0**.
This project adheres to [Semantic Versioning](http://semver.org/).

## 8.1.0 - 2017-10-09

- #402: Don't fail if captureStackTrace is not a function (#410) ([77ee965d9081faaf21650f266399f203f69533c5](https://github.com/auth0/node-jsonwebtoken/commit/77ee965d9081faaf21650f266399f203f69533c5))
- #403: Clarify error wording for "Expected object" error. (#409) ([bb27eb346f0ff675a320b2de16b391a7cfeadc58](https://github.com/auth0/node-jsonwebtoken/commit/bb27eb346f0ff675a320b2de16b391a7cfeadc58))
- Enhance audience check to verify against regular expressions (#398) ([81501a17da230af7b74a3f7535ab5cd3a19c8315](https://github.com/auth0/node-jsonwebtoken/commit/81501a17da230af7b74a3f7535ab5cd3a19c8315))


## 8.0.1 - 2017-09-12

- Remove `lodash.isarray` dependency (#394) ([7508e8957cb1c778f72fa9a363a7b135b3c9c36d](https://github.com/auth0/node-jsonwebtoken/commit/7508e8957cb1c778f72fa9a363a7b135b3c9c36d))

## 8.0.0 - 2017-09-06

**Breaking changes: See [Migration notes from v7](https://github.com/auth0/node-jsonwebtoken/wiki/Migration-Notes:-v7-to-v8)**

- docs: readme, migration notes ([12cd8f7f47224f904f6b8f39d1dee73775de4f6f](https://github.com/auth0/node-jsonwebtoken/commit/12cd8f7f47224f904f6b8f39d1dee73775de4f6f))
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ $ npm install jsonwebtoken

### jwt.sign(payload, secretOrPrivateKey, [options, callback])

(Asynchronous) If a callback is supplied, callback is called with the `err` or the JWT.
(Asynchronous) If a callback is supplied, the callback is called with the `err` or the JWT.

(Synchronous) Returns the JsonWebToken as string

@@ -50,7 +50,7 @@ There are no default values for `expiresIn`, `notBefore`, `audience`, `subject`,
Remember that `exp`, `nbf` and `iat` are **NumericDate**, see related [Token Expiration (exp claim)](#token-expiration-exp-claim)


The header can be customized via the `option.header` object.
The header can be customized via the `options.header` object.

Generated jwts will include an `iat` (issued at) claim by default unless `noTimestamp` is specified. If `iat` is inserted in the payload, it will be used instead of the real timestamp for calculating other things like `exp` given a timespan in `options.expiresIn`.

@@ -106,21 +106,21 @@ jwt.sign({

### jwt.verify(token, secretOrPublicKey, [options, callback])

(Asynchronous) If a callback is supplied, function acts asynchronously. Callback is passed the decoded payload if the signature and optional expiration, audience, or issuer are valid. If not, it will be passed the error.
(Asynchronous) If a callback is supplied, function acts asynchronously. The callback is called with the decoded payload if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will be called with the error.

(Synchronous) If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature (and, optionally, expiration, audience, issuer) are valid. If not, it will throw the error.
(Synchronous) If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will throw the error.

`token` is the JsonWebToken string

`secretOrPublicKey` is a string or buffer containing either the secret for HMAC algorithms, or the PEM
encoded public key for RSA and ECDSA.

As mentioned in [this comment](https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138), there are other libraries that expect base64 encoded secrets (random bytes encoded using base64), if that is your case you can pass `new Buffer(secret, 'base64')`, by doing this the secret will be decoded using base64 and the token verification will use the original random bytes.
As mentioned in [this comment](https://github.com/auth0/node-jsonwebtoken/issues/208#issuecomment-231861138), there are other libraries that expect base64 encoded secrets (random bytes encoded using base64), if that is your case you can pass `Buffer.from(secret, 'base64')`, by doing this the secret will be decoded using base64 and the token verification will use the original random bytes.

`options`

* `algorithms`: List of strings with the names of the allowed algorithms. For instance, `["HS256", "HS384"]`.
* `audience`: if you want to check audience (`aud`), provide a value here
* `audience`: if you want to check audience (`aud`), provide a value here. The audience can be checked against a string, a regular expression or a list of strings and/or regular expressions. Eg: `"urn:foo"`, `/urn:f[o]{2}/`, `[/urn:f[o]{2}/, "urn:bar"]`
* `issuer` (optional): string or array of strings of valid values for the `iss` field.
* `ignoreExpiration`: if `true` do not validate the expiration of the token.
* `ignoreNotBefore`...
6 changes: 4 additions & 2 deletions lib/JsonWebTokenError.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
var JsonWebTokenError = function (message, error) {
Error.call(this, message);
Error.captureStackTrace(this, this.constructor);
if(Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
this.name = 'JsonWebTokenError';
this.message = message;
if (error) this.inner = error;
@@ -9,4 +11,4 @@ var JsonWebTokenError = function (message, error) {
JsonWebTokenError.prototype = Object.create(Error.prototype);
JsonWebTokenError.prototype.constructor = JsonWebTokenError;

module.exports = JsonWebTokenError;
module.exports = JsonWebTokenError;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jsonwebtoken",
"version": "8.0.1",
"version": "8.1.0",
"description": "JSON Web Token implementation (symmetric and asymmetric)",
"main": "index.js",
"scripts": {
22 changes: 15 additions & 7 deletions sign.js
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ var sign_options_schema = {
subject: { isValid: isString, message: '"subject" must be a string' },
jwtid: { isValid: isString, message: '"jwtid" must be a string' },
noTimestamp: { isValid: isBoolean, message: '"noTimestamp" must be a boolean' },
keyid: { isValid: isString, message: '"keyid" must be a string' },
keyid: { isValid: isString, message: '"keyid" must be a string' }
};

var registered_claims_schema = {
@@ -29,16 +29,16 @@ var registered_claims_schema = {
nbf: { isValid: isNumber, message: '"nbf" should be a number of seconds' }
};

function validate(schema, unknown, object) {
function validate(schema, allowUnknown, object, parameterName) {
if (!isPlainObject(object)) {
throw new Error('Expected object');
throw new Error('Expected "' + parameterName + '" to be a plain object.');
}
Object.keys(object)
.forEach(function(key) {
var validator = schema[key];
if (!validator) {
if (!unknown) {
throw new Error('"' + key + '" is not allowed');
if (!allowUnknown) {
throw new Error('"' + key + '" is not allowed in "' + parameterName + '"');
}
return;
}
@@ -48,6 +48,14 @@ function validate(schema, unknown, object) {
});
}

function validateOptions(options) {
return validate(sign_options_schema, false, options, 'options');
}

function validatePayload(payload) {
return validate(registered_claims_schema, true, payload, 'payload');
}

var options_to_payload = {
'audience': 'aud',
'issuer': 'iss',
@@ -97,7 +105,7 @@ module.exports = function (payload, secretOrPrivateKey, options, callback) {
return failure(new Error('payload is required'));
} else if (isObjectPayload) {
try {
validate(registered_claims_schema, true, payload);
validatePayload(payload);
}
catch (error) {
return failure(error);
@@ -122,7 +130,7 @@ module.exports = function (payload, secretOrPrivateKey, options, callback) {
}

try {
validate(sign_options_schema, false, options);
validateOptions(options);
}
catch (error) {
return failure(error);
76 changes: 74 additions & 2 deletions test/jwt.asymmetric_signing.tests.js
Original file line number Diff line number Diff line change
@@ -175,6 +175,14 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should check audience using RegExp', function (done) {
jwt.verify(token, pub, { audience: /urn:f[o]{2}/ }, function (err, decoded) {
assert.isNotNull(decoded);
assert.isNull(err);
done();
});
});

it('should check audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:foo', 'urn:other'] }, function (err, decoded) {
assert.isNotNull(decoded);
@@ -183,6 +191,14 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should check audience in array using RegExp', function (done) {
jwt.verify(token, pub, { audience: ['urn:bar', /urn:f[o]{2}/, 'urn:other'] }, function (err, decoded) {
assert.isNotNull(decoded);
assert.isNull(err);
done();
});
});

it('should throw when invalid audience', function (done) {
jwt.verify(token, pub, { audience: 'urn:wrong' }, function (err, decoded) {
assert.isUndefined(decoded);
@@ -193,8 +209,18 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should throw when invalid audience using RegExp', function (done) {
jwt.verify(token, pub, { audience: /urn:bar/ }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
assert.instanceOf(err, jwt.JsonWebTokenError);
done();
});
});

it('should throw when invalid audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong'] }, function (err, decoded) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong', /urn:bar/] }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
@@ -224,6 +250,14 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should check audience using RegExp', function (done) {
jwt.verify(token, pub, { audience: /urn:f[o]{2}/ }, function (err, decoded) {
assert.isNotNull(decoded);
assert.isNull(err);
done();
});
});

it('should check audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:foo', 'urn:other'] }, function (err, decoded) {
assert.isNotNull(decoded);
@@ -232,6 +266,14 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should check audience in array using RegExp', function (done) {
jwt.verify(token, pub, { audience: ['urn:one', 'urn:other', /urn:f[o]{2}/] }, function (err, decoded) {
assert.isNotNull(decoded);
assert.isNull(err);
done();
});
});

it('should throw when invalid audience', function (done) {
jwt.verify(token, pub, { audience: 'urn:wrong' }, function (err, decoded) {
assert.isUndefined(decoded);
@@ -242,6 +284,16 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should throw when invalid audience using RegExp', function (done) {
jwt.verify(token, pub, { audience: /urn:wrong/ }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
assert.instanceOf(err, jwt.JsonWebTokenError);
done();
});
});

it('should throw when invalid audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong'] }, function (err, decoded) {
assert.isUndefined(decoded);
@@ -252,6 +304,16 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should throw when invalid audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong', /urn:alsowrong/] }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
assert.instanceOf(err, jwt.JsonWebTokenError);
done();
});
});

});

describe('when signing a token without audience', function () {
@@ -267,8 +329,18 @@ describe('Asymmetric Algorithms', function(){
});
});

it('should check audience using RegExp', function (done) {
jwt.verify(token, pub, { audience: /urn:wrong/ }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
assert.instanceOf(err, jwt.JsonWebTokenError);
done();
});
});

it('should check audience in array', function (done) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong'] }, function (err, decoded) {
jwt.verify(token, pub, { audience: ['urn:wrong', 'urn:morewrong', /urn:alsowrong/] }, function (err, decoded) {
assert.isUndefined(decoded);
assert.isNotNull(err);
assert.equal(err.name, 'JsonWebTokenError');
6 changes: 5 additions & 1 deletion verify.js
Original file line number Diff line number Diff line change
@@ -131,7 +131,11 @@ module.exports = function (jwtString, secretOrPublicKey, options, callback) {
var audiences = Array.isArray(options.audience)? options.audience : [options.audience];
var target = Array.isArray(payload.aud) ? payload.aud : [payload.aud];

var match = target.some(function(aud) { return audiences.indexOf(aud) != -1; });
var match = target.some(function(targetAudience) {
return audiences.some(function(audience) {
return audience instanceof RegExp ? audience.test(targetAudience) : audience === targetAudience;
});
});

if (!match)
return done(new JsonWebTokenError('jwt audience invalid. expected: ' + audiences.join(' or ')));