diff --git a/config.js b/config.js index 02b40681d..16353caaa 100644 --- a/config.js +++ b/config.js @@ -130,20 +130,18 @@ if (process.env.NODE_ENV === 'test' && !CONFIG.ROOT_URL) { if (CONFIG.JWT_SECRETS) { CONFIG.JWT_SECRETS = JSON.parse(CONFIG.JWT_SECRETS); -} - -if (process.env.NODE_ENV === 'test' && !CONFIG.JWT_SECRET) { - CONFIG.JWT_SECRET = 'keyboard cat'; } else if (!CONFIG.JWT_SECRET) { - throw new Error( - 'TALK_JWT_SECRET must be provided in the environment to sign/verify tokens' - ); -} + if (process.env.NODE_ENV === 'test') { + if (!CONFIG.JWT_ALG.startsWith('HS')) { + throw new Error('Providing a asymmetric signing/verfying algorithm without a corresponding secret is not permitted'); + } -// If this is not employing a HMAC based signing method, then we need to turn -// the secret into a buffer. -if (!CONFIG.JWT_ALG.startsWith('HS')) { - CONFIG.JWT_SECRET = Buffer.from(CONFIG.JWT_SECRET); + CONFIG.JWT_SECRET = 'keyboard cat'; + } else { + throw new Error( + 'TALK_JWT_SECRET must be provided in the environment to sign/verify tokens' + ); + } } //------------------------------------------------------------------------------ diff --git a/secrets.js b/secrets.js index 14b5fc922..b7044fa09 100644 --- a/secrets.js +++ b/secrets.js @@ -22,6 +22,10 @@ if (JWT_SECRETS) { throw new Error('when multiple keys are specified, kid\'s must be specified'); } + if (typeof secret.kid !== 'string' || secret.kid.length === 0) { + throw new Error('kid must be a unique string'); + } + // HMAC secrets do not have public/private keys. if (JWT_ALG.startsWith('HS')) { return new jwt.SharedSecret(secret, JWT_ALG); @@ -34,7 +38,7 @@ if (JWT_SECRETS) { return new jwt.AsymmetricSecret(secret, JWT_ALG); })); - debug(`loaded ${JWT_SECRET.length} ${JWT_ALG.startsWith('HS') ? 'shared' : 'asymmetric'} secrets`); + debug(`loaded ${JWT_SECRETS.length} ${JWT_ALG.startsWith('HS') ? 'shared' : 'asymmetric'} secrets`); } else if (JWT_SECRET) { if (JWT_ALG.startsWith('HS')) { module.exports.jwt = new jwt.SharedSecret({ diff --git a/services/jwt.js b/services/jwt.js index c5f69c2fd..d930971cb 100644 --- a/services/jwt.js +++ b/services/jwt.js @@ -1,4 +1,5 @@ const jwt = require('jsonwebtoken'); +const uniq = require('lodash/uniq'); /** * MultiSecret will take many secrets and provide a unified interface for @@ -6,6 +7,12 @@ const jwt = require('jsonwebtoken'); */ class MultiSecret { constructor(secrets) { + this.kids = secrets.map(({kid}) => kid); + + if (uniq(this.kids).length !== secrets.length) { + throw new Error('Duplicate kid\'s cannot be used to construct a MultiSecret'); + } + this.secrets = secrets; } @@ -86,7 +93,11 @@ class Secret { /** * SharedSecret is the HMAC based secret that's used for signing/verifying. */ -function SharedSecret({kid = undefined, secret}, algorithm) { +function SharedSecret({kid = undefined, secret = null}, algorithm) { + if (secret === null || secret.length === 0) { + throw new Error('Secret cannot have a zero length'); + } + return new Secret({ kid, signingKey: secret, diff --git a/services/passport.js b/services/passport.js index 542fc9471..8b5b3879b 100644 --- a/services/passport.js +++ b/services/passport.js @@ -195,7 +195,9 @@ const CheckBlacklisted = async (jwt) => { } // It wasn't a PAT! Check to see if it is valid anyways. - return checkGeneralTokenBlacklist(jwt); + await checkGeneralTokenBlacklist(jwt); + + return null; }; const JwtStrategy = require('passport-jwt').Strategy; @@ -257,11 +259,14 @@ passport.use(new JwtStrategy({ try { // Check to see if the token has been revoked - await CheckBlacklisted(jwt); + let user = await CheckBlacklisted(jwt); - // Try to get the user from the database or crack it from the token and - // plugin integrations. - let user = await UsersService.findOrCreateByIDToken(jwt.sub, {token, jwt}); + if (user === null) { + + // Try to get the user from the database or crack it from the token and + // plugin integrations. + user = await UsersService.findOrCreateByIDToken(jwt.sub, {token, jwt}); + } // Attach the JWT to the request. req.jwt = jwt; diff --git a/services/tokens.js b/services/tokens.js index 8184d47b5..9c9af34a4 100644 --- a/services/tokens.js +++ b/services/tokens.js @@ -93,7 +93,7 @@ module.exports = class TokenService { // Find the user. let user = await UserModel.findOne({ id: userID - }).select('tokens'); + }); if (!user || !user.tokens) { throw new errors.ErrAuthentication('user does not exist'); } @@ -108,6 +108,8 @@ module.exports = class TokenService { if (!token.active) { throw new errors.ErrAuthentication('token is not active'); } + + return user; } /**