diff --git a/lib/passport-http/strategies/digest.js b/lib/passport-http/strategies/digest.js index f0433e8..e86e466 100644 --- a/lib/passport-http/strategies/digest.js +++ b/lib/passport-http/strategies/digest.js @@ -66,7 +66,7 @@ function DigestStrategy(options, secret, validate) { options = {}; } if (!secret) throw new Error('HTTP Digest authentication strategy requires a secret function'); - + passport.Strategy.call(this); this.name = 'digest'; this._secret = secret; @@ -80,6 +80,7 @@ function DigestStrategy(options, secret, validate) { if (options.qop) { this._qop = (Array.isArray(options.qop)) ? options.qop : [ options.qop ]; } + this._passReqToCallback = options.passReqToCallback; } /** @@ -97,28 +98,28 @@ util.inherits(DigestStrategy, passport.Strategy); DigestStrategy.prototype.authenticate = function(req) { var authorization = req.headers['authorization']; if (!authorization) { return this.fail(this._challenge()); } - + var parts = authorization.split(' ') if (parts.length < 2) { return this.fail(400); } - + var scheme = parts[0] , params = parts.slice(1).join(' '); - + if (!/Digest/i.test(scheme)) { return this.fail(this._challenge()); } - + var creds = parse(params); if (Object.keys(creds).length === 0) { return this.fail(400); } - + if (!creds.username) { return this.fail(this._challenge()); } if (req.url !== creds.uri) { return this.fail(400); } - - + + var self = this; - + // Use of digest authentication requires a password (aka shared secret) known // to both the client and server, but not transported over the wire. This // secret is needed in order to compute the hashes required to authenticate @@ -128,10 +129,11 @@ DigestStrategy.prototype.authenticate = function(req) { // However, the user will only be successfully authenticated if the password // is correct, as indicated by the challenge response matching the computed // value. - this._secret(creds.username, function(err, user, password) { + + function secretCallback (err, user, password) { if (err) { return self.error(err); } if (!user) { return self.fail(self._challenge()); } - + var ha1; if (!creds.algorithm || creds.algorithm === 'MD5') { if (typeof password === 'object' && password.ha1) { @@ -151,7 +153,7 @@ DigestStrategy.prototype.authenticate = function(req) { } else { return self.fail(400); } - + var ha2; if (!creds.qop || creds.qop === 'auth') { ha2 = md5(req.method + ":" + creds.uri); @@ -166,7 +168,7 @@ DigestStrategy.prototype.authenticate = function(req) { } else { return self.fail(400); } - + var digest; if (!creds.qop) { digest = md5(ha1 + ":" + creds.nonce + ":" + ha2); @@ -175,7 +177,7 @@ DigestStrategy.prototype.authenticate = function(req) { } else { return self.fail(400); } - + if (creds.response != digest) { return self.fail(self._challenge()); } else { @@ -195,7 +197,13 @@ DigestStrategy.prototype.authenticate = function(req) { self.success(user); } } - }); + } + + if (this._passReqToCallback) { + this._secret(req, creds.username, secretCallback); + } else { + this._secret(creds.username, secretCallback); + } } /** @@ -206,7 +214,7 @@ DigestStrategy.prototype.authenticate = function(req) { DigestStrategy.prototype._challenge = function() { // TODO: For maximum flexibility, a mechanism for delegating the generation // of the nonce and opaque data to the application would be useful. - + var challenge = 'Digest realm="' + this._realm + '"'; if (this._domain) { challenge += ', domain="' + this._domain.join(' ') + '"'; @@ -221,7 +229,7 @@ DigestStrategy.prototype._challenge = function() { if (this._qop) { challenge += ', qop="' + this._qop.join(',') + '"'; } - + return challenge; } @@ -294,5 +302,5 @@ function md5(str, encoding){ /** * Expose `DigestStrategy`. - */ + */ module.exports = DigestStrategy;