diff --git a/lib/passport/middleware/authenticate.js b/lib/passport/middleware/authenticate.js index ebb89e14..a88275b9 100644 --- a/lib/passport/middleware/authenticate.js +++ b/lib/passport/middleware/authenticate.js @@ -82,6 +82,16 @@ module.exports = function authenticate(name, options, callback) { var failures = []; function allFailed() { + if (callback) { + if (failures.length == 1) { + return callback(null, false, failures[0].challenge, failures[0].status); + } else { + var challenges = failures.map(function(f) { return f.challenge; }); + var statuses = failures.map(function(f) { return f.status; }) + return callback(null, false, challenges, statuses); + } + } + // Strategies are ordered by priority. For the purpose of flashing a // message, the first failure will be displayed. var failure = failures[0] || {} @@ -190,10 +200,6 @@ module.exports = function authenticate(name, options, callback) { }); } delegate.fail = function(challenge, status) { - if (callback) { - return callback(null, false, challenge, status); - } - // push this failure into the accumulator and attempt authentication // using the next strategy failures.push({ challenge: challenge, status: status }); diff --git a/test/middleware/authenticate-test.js b/test/middleware/authenticate-test.js index cb3e6d6e..3dbe3c6c 100644 --- a/test/middleware/authenticate-test.js +++ b/test/middleware/authenticate-test.js @@ -3041,4 +3041,116 @@ vows.describe('authenticate').addBatch({ }, }, + 'with a single API strategy failing with challenge and status using custom callback': { + topic: function() { + var self = this; + var passport = new Passport(); + passport.use('basic', new MockBasicStrategy({ fail: true, statusCode: 400 })); + var callback = function(err, user, challenge, status) { + this.done(err, user, challenge, status); + } + var context = {}; + + var authenticate = passport.authenticate('basic', callback.bind(context)); + process.nextTick(function () { + self.callback(null, authenticate, context); + }); + }, + + 'when handling a request': { + topic: function(authenticate, context) { + var self = this; + var req = new MockRequest(); + var res = new MockResponse(); + context.done = function(err, user, challenge, status) { + self.callback(err, req, res, user, challenge, status); + } + + function next(err) { + self.callback(new Error('should not be called')); + } + process.nextTick(function () { + authenticate(req, res, next) + }); + }, + + 'should not generate an error' : function(err, req, res) { + assert.isNull(err); + }, + 'should not set user on request' : function(err, req, res, user, challenge, status) { + assert.isUndefined(req.user); + }, + 'should pass user to callback as false' : function(err, req, res, user, challenge, status) { + assert.isFalse(user); + }, + 'should pass challenge to callback' : function(err, req, res, user, challenge, status) { + assert.strictEqual(challenge, 'Basic foo'); + }, + 'should pass status to callback' : function(err, req, res, user, challenge, status) { + assert.strictEqual(status, 400); + }, + }, + }, + + 'with a multiple API strategies failing with default status using custom callback': { + topic: function() { + var self = this; + var passport = new Passport(); + passport.use('basic', new MockBasicStrategy({ fail: true })); + passport.use('digest', new MockDigestStrategy({ fail: true })); + passport.use('nc', new MockNoChallengeStrategy({ fail: true })); + var callback = function(err, user, challenge, status) { + this.done(err, user, challenge, status); + } + var context = {}; + + var authenticate = passport.authenticate(['basic', 'nc', 'digest'], callback.bind(context)); + process.nextTick(function () { + self.callback(null, authenticate, context); + }); + }, + + 'when handling a request': { + topic: function(authenticate, context) { + var self = this; + var req = new MockRequest(); + var res = new MockResponse(); + context.done = function(err, user, challenge, status) { + self.callback(err, req, res, user, challenge, status); + } + + function next(err) { + self.callback(new Error('should not be called')); + } + process.nextTick(function () { + authenticate(req, res, next) + }); + }, + + 'should not generate an error' : function(err, req, res) { + assert.isNull(err); + }, + 'should not set user on request' : function(err, req, res, user, challenge, status) { + assert.isUndefined(req.user); + }, + 'should pass user to callback as false' : function(err, req, res, user, challenge, status) { + assert.isFalse(user); + }, + 'should pass challenges callback' : function(err, req, res, user, challenge, status) { + assert.isArray(challenge); + assert.lengthOf(challenge, 3); + assert.equal(challenge[0], 'Basic foo'); + assert.isUndefined(challenge[1]); + assert.equal(challenge[2], 'Digest foo'); + }, + 'should pass statuses callback' : function(err, req, res, user, challenge, status) { + assert.isArray(status); + assert.lengthOf(status, 3); + assert.isUndefined(status[0]); + assert.isUndefined(status[1]); + assert.isUndefined(status[2]); + }, + }, + }, + }).export(module);