Skip to content

Commit

Permalink
Merge 3a011a4 into 2327a36
Browse files Browse the repository at this point in the history
  • Loading branch information
sjudson committed Jul 28, 2018
2 parents 2327a36 + 3a011a4 commit 4941b0a
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 23 deletions.
43 changes: 43 additions & 0 deletions lib/authenticator.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,49 @@ Authenticator.prototype.authenticate = function(strategy, options, callback) {
return this._framework.authenticate(this, strategy, options, callback);
};

/**
* Middleware that uses the given `configurator` to setup a strategy, which it
* will use authenticate a request, with optional `options` and `callback`.
*
* Examples:
*
* function config(req, done) {
* var lstrategy = new LocalStrategy(function(username, password, done) {
* var db = dbs[req.params.directory];
*
* db.users.verify(username, password, function(err, user) {
* if (err) { return next(err); }
* if (!user) { return done(null, false); }
* return done(null, user);
* });
* }
*
* return done(null, lstrategy);
* }
*
* passport.authenticateWith(config, { successRedirect: '/', failureRedirect: '/login' })(req, res);
*
* passport.authenticateWith(config, function(err, user) {
* if (!user) { return res.redirect('/login'); }
* res.end('AuthenticateWithd!');
* })(req, res);
*
* passport.authenticateWith(config, { session: false })(req, res);
*
* app.get('/authenticate', passport.authenticateWith(config), function(req, res) {
* res.json(req.user);
* });
*
* @param {Strategy} configurator
* @param {Object} options
* @param {Function} callback
* @return {Function} middleware
* @api public
*/
Authenticator.prototype.authenticateWith = function(configurator, options, callback) {
return this._framework.authenticateWith(this, configurator, options, callback);
};

/**
* Middleware that will authorize a third-party account using the given
* `strategy` name, with optional `options`.
Expand Down
6 changes: 4 additions & 2 deletions lib/framework/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Module dependencies.
*/
var initialize = require('../middleware/initialize')
, authenticate = require('../middleware/authenticate');
, authenticate = require('../middleware/authenticate')
, authenticateWith = require('../middleware/authenticateWith');

/**
* Framework support for Connect/Express.
Expand All @@ -22,7 +23,8 @@ exports = module.exports = function() {

return {
initialize: initialize,
authenticate: authenticate
authenticate: authenticate,
authenticateWith: authenticateWith
};
};

Expand Down
45 changes: 30 additions & 15 deletions lib/middleware/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
* Module dependencies.
*/
var http = require('http')
, Strategy = require('passport-strategy')
, IncomingMessageExt = require('../http/request')
, AuthenticationError = require('../errors/authenticationerror');



/**
* Authenticates requests.
*
Expand Down Expand Up @@ -61,13 +63,13 @@ var http = require('http')
*
* passport.authenticate('twitter');
*
* @param {String|Array} name
* @param {Strategy|String|Array} method
* @param {Object} options
* @param {Function} callback
* @return {Function}
* @api public
*/
module.exports = function authenticate(passport, name, options, callback) {
module.exports = function authenticate(passport, method, options, callback) {
if (typeof options == 'function') {
callback = options;
options = {};
Expand All @@ -76,18 +78,18 @@ module.exports = function authenticate(passport, name, options, callback) {

var multi = true;

// Cast `name` to an array, allowing authentication to pass through a chain of
// strategies. The first strategy to succeed, redirect, or error will halt
// the chain. Authentication failures will proceed through each strategy in
// series, ultimately failing if all strategies fail.
// Cast `method` to an array, allowing authentication to pass through a
// chain of strategies. The first strategy to succeed, redirect, or error
// will halt the chain. Authentication failures will proceed through each
// strategy in series, ultimately failing if all strategies fail.
//
// This is typically used on API endpoints to allow clients to authenticate
// using their preferred choice of Basic, Digest, token-based schemes, etc.
// It is not feasible to construct a chain of multiple strategies that involve
// redirection (for example both Facebook and Twitter), since the first one to
// redirect will halt the chain.
if (!Array.isArray(name)) {
name = [ name ];
if (!Array.isArray(method)) {
method = [ method ];
multi = false;
}

Expand Down Expand Up @@ -175,18 +177,22 @@ module.exports = function authenticate(passport, name, options, callback) {
}

(function attempt(i) {
var layer = name[i];
var layer = method[i];
// If no more strategies exist in the chain, authentication has failed.
if (!layer) { return allFailed(); }

// Get the strategy, which will be used as prototype from which to create
// a new instance. Action functions will then be bound to the strategy
// within the context of the HTTP request/response pair.
var prototype = passport._strategy(layer);
if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); }

var strategy = Object.create(prototype);

var strategy, prototype;
if (typeof layer.authenticate == 'function') {
strategy = layer;
} else { // typeof layer == 'string'
prototype = passport._strategy(layer);
if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); }

strategy = Object.create(prototype);
}

// ----- BEGIN STRATEGY AUGMENTATION -----
// Augment the new strategy instance with action functions. These action
Expand Down Expand Up @@ -242,7 +248,16 @@ module.exports = function authenticate(passport, name, options, callback) {
}
if (options.assignProperty) {
req[options.assignProperty] = user;
return next();
if (options.authInfo !== false) {
passport.transformAuthInfo(info, req, function(err, tinfo) {
if (err) { return next(err); }
req.authInfo = tinfo;
return next();
});
return;
} else {
return next();
}
}

req.logIn(user, options, function(err) {
Expand Down
62 changes: 62 additions & 0 deletions lib/middleware/authenticateWith.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Module dependencies.
*/
var authenticate = require('./authenticate')
, Strategy = require('passport-strategy')



/**
* Dynamically configures methods for authenticating requests.
*
* Employs the `configurator` to dynamically determine either a Strategy or
* list of strategy names with which to authenticate the request. This allows
* the authentication to be undertaken with context of the individual request.
* If the configurator returns a list of strategy names, those strategies must
* be preconfigured, as for the standard `authenticate` middleware.
*
* See the superior `authenticate` middleware for more information and details.
*
* @param {Function} configurator
* @param {Object} options
* @param {Function} callback
* @return {Function}
* @api public
*/
module.exports = function authenticateWith(passport, configurator, options, callback) {
if (typeof options == 'function') {
callback = options;
options = {};
}
options = options || {};

// The default configurator will cause `authenticate` to fail and handle
// that failure as per how it is set up.
configurator = configurator || function(req, done) { return done(); }

return function authenticateWith(req, res, next) {

function configured(err, method) {
if (err) { return next(err); }

// If configuration does not return a Strategy or name(s) of strategies,
// we will pass up an empty array, which `authenticate` will handle using
// the preferred failure mode. This provides the least behavioral
// divergence between this middleware and `authenticate`.
method = method || [];

return authenticate(passport, method, options, callback)(req, res, next);
}

try {
var arity = configurator.length;
if (arity == 3) {
configurator(req, options, configured);
} else { // arity == 2
configurator(req, configured);
}
} catch (ex) {
return next(ex);
}
};
};
7 changes: 4 additions & 3 deletions test/authenticator.middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,10 @@ describe('Authenticator', function() {
expect(request.account.id).to.equal('1');
expect(request.account.username).to.equal('jaredhanson');
});

it('should not set authInfo', function() {
expect(request.authInfo).to.be.undefined;

it('should set authInfo', function() {
expect(request.authInfo).to.be.an('object');
expect(Object.keys(request.authInfo)).to.have.length(0);
});
});

Expand Down
7 changes: 4 additions & 3 deletions test/middleware/authenticate.success.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ describe('middleware/authenticate', function() {
expect(request.account.id).to.equal('1');
expect(request.account.username).to.equal('jaredhanson');
});

it('should not set authInfo', function() {
expect(request.authInfo).to.be.undefined;

it('should set authInfo', function() {
expect(request.authInfo).to.be.an('object');
expect(Object.keys(request.authInfo)).to.have.length(0);
});
});

Expand Down

0 comments on commit 4941b0a

Please sign in to comment.