UNPKG

@mestrak/passport-multi-jwt

Version:

Passport authentication strategy using JSON Web Tokens with multiple providers. Fully backwards compatible with passport-jwt.

166 lines (143 loc) 6.79 kB
var passport = require('passport-strategy') , auth_hdr = require('./auth_header') , util = require('util') , url = require('url') , assign = require('./helpers/assign.js'); /** * Strategy constructor * * @param options * secretOrKey: String or buffer containing the secret or PEM-encoded public key. Required unless secretOrKeyProvider is provided. * secretOrKeyProvider: callback in the format secretOrKeyProvider(request, rawJwtToken, done)`, * which should call done with a secret or PEM-encoded public key * (asymmetric) for the given undecoded jwt token string and request * combination. done has the signature function done(err, secret). * REQUIRED unless `secretOrKey` is provided. * jwtFromRequest: (REQUIRED) Function that accepts a reqeust as the only parameter and returns the either JWT as a string or null * issuer: If defined issuer will be verified against this value * audience: If defined audience will be verified against this value * algorithms: List of strings with the names of the allowed algorithms. For instance, ["HS256", "HS384"]. * ignoreExpiration: if true do not validate the expiration of the token. * passReqToCallback: If true the, the verify callback will be called with args (request, jwt_payload, done_callback). * @param verify - Verify callback with args (jwt_payload, done_callback) if passReqToCallback is false, * (request, jwt_payload, done_callback) if true. */ function JwtStrategy(options, verify) { // for backwards compatibility with passport-jwt // options does not need to be an array if (!Array.isArray(options)) { options = [options]; } passport.Strategy.call(this); this.name = 'jwt'; this._config = new Array(options.length); for (let i = 0; i < options.length; i++) { const optionsItem = options[i]; this._config[i] = {}; let configItem = this._config[i]; configItem._secretOrKeyProvider = optionsItem.secretOrKeyProvider; if (optionsItem.secretOrKey) { if (configItem._secretOrKeyProvider) { throw new TypeError('JwtStrategy has been given both a secretOrKey and a secretOrKeyProvider'); } configItem._secretOrKeyProvider = function (request, rawJwtToken, done) { done(null, optionsItem.secretOrKey) }; } if (!configItem._secretOrKeyProvider) { throw new TypeError('JwtStrategy requires a secret or key'); } configItem._verify = verify; if (!configItem._verify) { throw new TypeError('JwtStrategy requires a verify callback'); } configItem._jwtFromRequest = optionsItem.jwtFromRequest; if (!configItem._jwtFromRequest) { throw new TypeError('JwtStrategy requires a function to retrieve jwt from requests (see option jwtFromRequest)'); } configItem._passReqToCallback = optionsItem.passReqToCallback; var jsonWebTokenOptions = optionsItem.jsonWebTokenOptions || {}; //for backwards compatibility, still allowing you to pass //audience / issuer / algorithms / ignoreExpiration //on the options. configItem._verifOpts = assign({}, jsonWebTokenOptions, { audience: optionsItem.audience, issuer: optionsItem.issuer, algorithms: optionsItem.algorithms, ignoreExpiration: !!optionsItem.ignoreExpiration }); // return configItem; }; } util.inherits(JwtStrategy, passport.Strategy); /** * Allow for injection of JWT Verifier. * * This improves testability by allowing tests to cleanly isolate failures in the JWT Verification * process from failures in the passport related mechanics of authentication. * * Note that this should only be replaced in tests. */ JwtStrategy.JwtVerifier = require('./verify_jwt'); /** * Authenticate request based on JWT obtained from header or post body */ JwtStrategy.prototype.authenticate = function (req, options) { var self = this; var promises = []; self._config.forEach(function(configItem){ promises.push(new Promise((resolve, reject) => { var token = configItem._jwtFromRequest(req); if (!token) { reject({error:new Error("No auth token"),cause:"fail"}); }else{ configItem._secretOrKeyProvider(req, token, function(secretOrKeyError, secretOrKey) { if (secretOrKeyError) { reject({error:secretOrKeyError,cause:"fail"}) } else { // Verify the JWT JwtStrategy.JwtVerifier(token, secretOrKey, configItem._verifOpts, function(jwt_err, payload) { if (jwt_err) { reject({error:jwt_err,cause:"fail"}); } else { // Pass the parsed token to the user var verified = function(err, user, info) { if(err) { reject({error:err,cause:"error"}); } else if (!user) { reject({error:info,cause:"fail"}); } else { resolve({user:user, info:info}); } }; try { if (configItem._passReqToCallback) { configItem._verify(req, payload, verified); } else { configItem._verify(payload, verified); } } catch(ex) { reject({error:ex,cause:"error"}); } } }); } }); } })); }); Promise.any(promises).then(function(data){ self.success(data.user,data.info); }).catch(function(errors){ var errObject = errors.errors[0]; if(errObject.cause == "fail"){ self.fail(errObject.error); }else if(errObject.cause == "error"){ self.error(errObject.error); } }); }; /** * Export the Jwt Strategy */ module.exports = JwtStrategy;