diff --git a/README.md b/README.md index 052980e..cc41192 100644 --- a/README.md +++ b/README.md @@ -234,9 +234,40 @@ ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm none | No digital signature or MAC value included +## Key Lookup + +```javascript + var claims = {hello: 'world'} + + var keys = [ + {kid: 'key1', secret: '12345'}, + {kid: 'key2',secret: 'abcd'} + ]; + var currentKey = 0 + + // create a token + var token = new nJwt.Jwt(claims) + .setSigningAlgorithm('HS256') + .setSigningKey(keys[currentKey].secret) + .setSigningKeyId(keys[currentKey].kid) + .compact(); + + // Parse the tokent + var jwt = new nJwt.Parser().parse(token); + // lookup the key + var found = keys.find(k => k.kid === jwt.header.kid) + // then verify + var verifier = new nJwt.Verifier() + .setSigningAlgorithm('HS256') + .setSigningKey(found.secret) + .verify(token, function (err, res) { + if (res.body.hello !== claims.hello) + throw(new Error('lookup didnt work')) + }); +``` + ## Unsupported features The following features are not yet supported by this library: * Encrypting the JWT (aka JWE) -* Signing key resolver (using the `kid` field) diff --git a/index.js b/index.js index 87246ea..1f62c26 100644 --- a/index.js +++ b/index.js @@ -192,6 +192,11 @@ Jwt.prototype.setSigningKey = function setSigningKey(key) { this.signingKey = key; return this; }; +Jwt.prototype.setSigningKeyId = function setSigningKeyId(kid) { + this.header.kid = kid; + return this; +}; + Jwt.prototype.setSigningAlgorithm = function setSigningAlgorithm(alg) { if(!this.isSupportedAlg(alg)){ throw new JwtError(properties.errors.UNSUPPORTED_SIGNING_ALG); @@ -258,6 +263,9 @@ Jwt.prototype.isNotBefore = function() { }; function Parser(options){ + if(!(this instanceof Parser)){ + return new Parser(options); + } return this; } @@ -328,12 +336,16 @@ Verifier.prototype.verify = function verify(jwtString,cb){ var done = handleError.bind(null,cb); - try { - jwt = new Parser().parse(jwtString); - } catch(e) { - return done(e); + if (jwtString instanceof Jwt) { + jwt = jwtString; + // console.log(jwt) + } else { + try { + jwt = new Parser().parse(jwtString); + } catch(e) { + return done(e); + } } - var body = jwt.body; var header = jwt.header; var signature = jwt.signature; @@ -402,6 +414,7 @@ var jwtLib = { Jwt: Jwt, JwtBody: JwtBody, JwtHeader: JwtHeader, + Parser: Parser, Verifier: Verifier, base64urlEncode: base64urlEncode, base64urlUnescape:base64urlUnescape, diff --git a/test/jwt.js b/test/jwt.js index cecbfb5..8a952c0 100644 --- a/test/jwt.js +++ b/test/jwt.js @@ -83,6 +83,15 @@ describe('Jwt',function() { }); }); + describe('.setSigningKeyId()',function(){ + it('should accept a kid',function(){ + var kid = '1234' + var jwt = new nJwt.Jwt({}, false) + .setSigningKeyId(kid); + assert.equal(jwt.header.kid, kid); + }); + }); + describe('.sign()',function(){ it('should throw if you give it an unknown algoritm',function(){ assert.throws(function(){ diff --git a/test/key-lookup.js b/test/key-lookup.js new file mode 100644 index 0000000..4b2fefa --- /dev/null +++ b/test/key-lookup.js @@ -0,0 +1,75 @@ +var assert = require('chai').assert; +var uuid = require('uuid'); +var nJwt = require('../'); + +describe('demonstrate a key lookup on verify', function () { + describe('and given an signed token', function () { + var result; + var claims = {hello: 'world'} + var keys = [ + {kid: 'key1', secret: '12345'}, + {kid: 'key2',secret: 'abcd'} + ]; + var currentKey = 0 + var expectedSecret = keys[currentKey].secret + + var token = new nJwt.Jwt(claims) + .setSigningAlgorithm('HS256') + .setSigningKey(expectedSecret) + .setSigningKeyId(keys[currentKey].kid) + .compact(); + + var jwt = new nJwt.Parser().parse(token); + var found = keys.find(k => k.kid === jwt.header.kid) + + assert.equal(found.secret, expectedSecret); + + before(function (done) { + var verifier = new nJwt.Verifier() + .setSigningAlgorithm('HS256') + .setSigningKey(found.secret) + verifier.verify(token, function (err, res) { + result = [err, res]; + done(); + }); + }); + + it('the jwt should be equal', function () { + assert.equal(result[1].body.hello, claims.hello); + }); + }); + + + describe('demo key lookup', function () { + var claims = {hello: 'world'} + + var keys = [ + {kid: 'key1', secret: '12345'}, + {kid: 'key2',secret: 'abcd'} + ]; + var currentKey = 0 + + // ENCODE + // create a token + var token = new nJwt.Jwt(claims) + .setSigningAlgorithm('HS256') + .setSigningKey(keys[currentKey].secret) + .setSigningKeyId(keys[currentKey].kid) + .compact(); + + // DECODE + // Parse the tokent + var jwt = new nJwt.Parser().parse(token); + // lookup the key + var found = keys.find(k => k.kid === jwt.header.kid) + // then verify + var verifier = new nJwt.Verifier() + .setSigningAlgorithm('HS256') + .setSigningKey(found.secret) + .verify(token, function (err, res) { + if (res.body.hello !== claims.hello) + throw(new Error('lookup didnt work')) + }); + }); + +}); \ No newline at end of file diff --git a/test/parser.js b/test/parser.js new file mode 100644 index 0000000..c1628ee --- /dev/null +++ b/test/parser.js @@ -0,0 +1,43 @@ +var assert = require('chai').assert; +var uuid = require('uuid'); +var nJwt = require('../'); + + +describe('Parser', function () { + it('should construct itself if called without new', function () { + assert(nJwt.Parser() instanceof nJwt.Parser); + }); +}); + +describe('Parser.parse(token)', function () { + var result = null + var claims = { hello: 'world' } + var token = new nJwt.Jwt(claims, false) + .setSigningAlgorithm('none') + .compact(); + it('should parse a valid token', function () { + var jwt = new nJwt.Parser().parse(token); + assert.equal(jwt.body.hello, claims.hello); + }); + +}); + +describe('Parser.parse(token, cb)', function () { + var result = null + var claims = { hello: 'world' } + before(function (done) { + var token = new nJwt.Jwt(claims, false) + .setSigningAlgorithm('none') + .compact(); + var parser = nJwt.Parser(); + parser.parse(token, function (err, res) { + result = [err, res]; + done(); + }); + }); + it('should parse a valid token', function () { + assert.isNull(result[0], 'An error was not returned'); + assert.equal(result[1].body.hello, claims.hello); + }); + +}); \ No newline at end of file diff --git a/test/verifier.js b/test/verifier.js index de3fd01..6b2f801 100644 --- a/test/verifier.js +++ b/test/verifier.js @@ -328,4 +328,27 @@ describe('Verifier().verify() ',function(){ assert.equal(result[0].userMessage,properties.errors.SIGNATURE_MISMTACH); }); }); + + describe('verify an existing Jwt object', function () { + var result = null + var claims = { hello: 'world' } + + before(function(done){ + var token = new nJwt.Jwt(claims, false) + .setSigningAlgorithm('none') + .compact(); + var jwt = new nJwt.Parser().parse(token); + var verifier = new nJwt.Verifier() + .setSigningAlgorithm('none') + verifier.verify(jwt, function(err,res){ + result = [err,res]; + done(); + }); + }); + it('should accept a valid Jwt as first param', function () { + assert.equal(result[1].body.hello, claims.hello); + }); + + }); + });