Skip to content

Commit

Permalink
Support custom callback in multiple strategy failure case.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredhanson committed Jul 11, 2012
1 parent 6d597a3 commit e25e9c1
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 4 deletions.
14 changes: 10 additions & 4 deletions lib/passport/middleware/authenticate.js
Expand Up @@ -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] || {}
Expand Down Expand Up @@ -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 });
Expand Down
112 changes: 112 additions & 0 deletions test/middleware/authenticate-test.js
Expand Up @@ -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);

0 comments on commit e25e9c1

Please sign in to comment.