From 35164fa5bacf72e3dddb9799bfe7460571f6ef16 Mon Sep 17 00:00:00 2001 From: Ian Fox Date: Tue, 6 Jun 2017 15:06:48 -0700 Subject: [PATCH] fix(tokens): Use URL-safe values, add toJson --- lib/generateToken.js | 6 +++--- lib/token.js | 17 +++++++++++++++++ package.json | 1 + test/lib/generateToken.test.js | 4 ++-- test/lib/token.test.js | 27 ++++++++++++++++++++++++--- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/lib/generateToken.js b/lib/generateToken.js index 175262cb..aa3a4e22 100644 --- a/lib/generateToken.js +++ b/lib/generateToken.js @@ -2,8 +2,8 @@ const nodeify = require('./nodeify'); const crypto = require('crypto'); +const base64url = require('base64url'); const ALGORITHM = 'sha256'; -const ENCODING = 'base64'; const TOKEN_LENGTH = 256; // Measured in bits /** @@ -13,7 +13,7 @@ const TOKEN_LENGTH = 256; // Measured in bits */ function generateValue() { return nodeify.withContext(crypto, 'randomBytes', [TOKEN_LENGTH / 8]) - .then(buffer => buffer.toString(ENCODING)); + .then(buffer => base64url(buffer.toString())); } /** @@ -22,7 +22,7 @@ function generateValue() { * @return {String} Hashed value */ function hashValue(value) { - return crypto.createHash(ALGORITHM).update(value).digest(ENCODING); + return base64url(crypto.createHash(ALGORITHM).update(value).digest()); } module.exports = { diff --git a/lib/token.js b/lib/token.js index dff1b3cf..c4678c28 100644 --- a/lib/token.js +++ b/lib/token.js @@ -39,6 +39,23 @@ class TokenModel extends BaseModel { return model; }); } + + /** + * Get the token as JSON, including value if it exists + * @method toJson + * @return {Object} + */ + toJson() { + const output = super.toJson(); + + if (this.value) { + output.value = this.value; + } + + delete output.hash; + + return output; + } } module.exports = TokenModel; diff --git a/package.json b/package.json index 2a3d19ef..a12d15e8 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "async": "^2.0.1", + "base64url": "^2.0.0", "compare-versions": "^3.0.0", "hoek": "^4.0.1", "iron": "^4.0.1", diff --git a/test/lib/generateToken.test.js b/test/lib/generateToken.test.js index a885120d..8e7166b3 100644 --- a/test/lib/generateToken.test.js +++ b/test/lib/generateToken.test.js @@ -9,7 +9,7 @@ sinon.assert.expose(assert, { prefix: '' }); describe('generateToken', () => { const RANDOM_BYTES = 'some random bytes'; // Result of passing 'some random bytes' through a sha256 hash, in base64 - const HASH = 'mF0EvjvxWMrVz5ZGJcnbe0ZPooUlv/DAB9VrV6bmZmg='; + const HASH = 'mF0EvjvxWMrVz5ZGJcnbe0ZPooUlv_DAB9VrV6bmZmg'; let firstValue; it('generates a value', () => @@ -17,7 +17,7 @@ describe('generateToken', () => { .then((value) => { firstValue = value; // Check that it's a base64 value of the right length - assert.match(value, /[a-zA-Z0-9+/]{43}=/); + assert.match(value, /[a-zA-Z0-9_-]{43}/); })); it('generates a different value on a second call', () => { diff --git a/test/lib/token.test.js b/test/lib/token.test.js index d09fd6ef..32120bf9 100644 --- a/test/lib/token.test.js +++ b/test/lib/token.test.js @@ -8,7 +8,6 @@ const schema = require('screwdriver-data-schema'); sinon.assert.expose(assert, { prefix: '' }); describe('Token Model', () => { - const password = 'password'; let datastore; let generateTokenMock; let BaseModel; @@ -47,8 +46,7 @@ describe('Token Model', () => { id: 6789, name: 'Mobile client auth token', description: 'For the mobile app', - lastUsed: '2017-05-10T01:49:59.327Z', - password + lastUsed: '2017-05-10T01:49:59.327Z' }; token = new TokenModel(createConfig); }); @@ -102,4 +100,27 @@ describe('Token Model', () => { }); }); }); + + describe('toJson', () => { + const expected = { + userId: 12345, + id: 6789, + name: 'Mobile client auth token', + description: 'For the mobile app', + lastUsed: '2017-05-10T01:49:59.327Z' + }; + const value = 'tokenValue'; + + it('functions normally if no value is present', () => { + const output = token.toJson(); + + assert.deepEqual(output, expected); + }); + + it('adds the value field if present', () => { + token.value = value; + + assert.deepEqual(token.toJson(), Object.assign({}, expected, { value })); + }); + }); });