Comparing version 0.1.0 to 0.1.1
@@ -6,3 +6,7 @@ /** | ||
, path = require('path') | ||
, Strategy = require('./strategy'); | ||
, util = require('util') | ||
, Strategy = require('./strategy') | ||
, SessionStrategy = require('./strategies/session') | ||
, initialize = require('./middleware/initialize') | ||
, authenticate = require('./middleware/authenticate'); | ||
@@ -16,42 +20,217 @@ | ||
function Passport() { | ||
this._strategies = []; | ||
this._middleware = []; | ||
this._key = 'passport'; | ||
this._strategies = {}; | ||
this._serializers = []; | ||
this._deserializers = []; | ||
this.use(new SessionStrategy()); | ||
}; | ||
// TODO: Make it optional to name a strategy. If not named, it uses its default | ||
// name | ||
/** | ||
* Utilize the given `strategy` with optional `name`, overridding the strategy's | ||
* default name. | ||
* | ||
* Examples: | ||
* | ||
* passport.use(new TwitterStrategy(...)); | ||
* | ||
* passport.use('api', new http.BasicStrategy(...)); | ||
* | ||
* @param {String|Strategy} name | ||
* @param {Strategy} strategy | ||
* @return {Passport} for chaining | ||
* @api public | ||
*/ | ||
Passport.prototype.use = function(name, strategy) { | ||
this._strategies.push(strategy); | ||
if (!strategy) { | ||
strategy = name; | ||
name = strategy.name; | ||
} | ||
if (!name) throw new Error('authentication strategies must have a name'); | ||
var middleware = strategy.middleware; | ||
if (middleware) { | ||
if (Array.isArray(middleware)) { | ||
this._middleware = this._middleware.concat(middleware); | ||
} else { | ||
this._middleware.push(middleware); | ||
} | ||
} | ||
this._strategies[name] = strategy; | ||
return this; | ||
}; | ||
Passport.prototype.middleware = function() { | ||
var self = this; | ||
/** | ||
* Passport's primary initialization middleware. | ||
* | ||
* This middleware must be in use by the Connect/Express application for | ||
* Passport to operate. | ||
* | ||
* Examples: | ||
* | ||
* app.configure(function() { | ||
* app.use(passport.initialize()); | ||
* }); | ||
* | ||
* @return {Function} middleware | ||
* @api public | ||
*/ | ||
Passport.prototype.initialize = function() { | ||
return initialize().bind(this); | ||
} | ||
return function(req, res, next) { | ||
var stack = self._middleware; | ||
/** | ||
* Middleware that will restore login state from a session. | ||
* | ||
* Web applications typically use sessions to maintain login state between | ||
* requests. For example, a user will authenticate by entering credentials into | ||
* a form which is submitted to the server. If the credentials are valid, a | ||
* login session is established by setting a cookie containing a session | ||
* identifier in the user's web browser. The web browser will send this cookie | ||
* in subsequent requests to the server, allowing a session to be maintained. | ||
* | ||
* If sessions are being utilized, and a login session has been established, | ||
* this middleware will populate `req.user` with the current user. | ||
* | ||
* Note that sessions are not strictly required for Passport to operate. | ||
* However, as a general rule, most web applications will make use of sessions. | ||
* An exception to this rule would be an API server, which expects each HTTP | ||
* request to provide credentials in an Authorization header. | ||
* | ||
* Examples: | ||
* | ||
* app.configure(function() { | ||
* app.use(connect.cookieParser()); | ||
* app.use(connect.session({ secret: 'keyboard cat' })); | ||
* app.use(passport.initialize()); | ||
* app.use(passport.session()); | ||
* }); | ||
* | ||
* @return {Function} middleware | ||
* @api public | ||
*/ | ||
Passport.prototype.session = function() { | ||
return this.authenticate('session'); | ||
} | ||
/** | ||
* Middleware that will authenticate a request using the given `strategy` name, | ||
* with optional `options` and `callback`. | ||
* | ||
* Examples: | ||
* | ||
* passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })(req, res); | ||
* | ||
* passport.authenticate('local', function(err, user) { | ||
* if (!user) { return res.redirect('/login'); } | ||
* res.end('Authenticated!'); | ||
* })(req, res); | ||
* | ||
* passport.authenticate('basic', { session: false })(req, res); | ||
* | ||
* app.get('/auth/twitter', passport.authenticate('twitter'), function(req, res) { | ||
* // request will be redirected to Twitter | ||
* }); | ||
* app.get('/auth/twitter/callback', passport.authenticate('twitter'), function(req, res) { | ||
* res.json(req.user); | ||
* }); | ||
* | ||
* @param {String} strategy | ||
* @param {Object} options | ||
* @param {Function} callback | ||
* @return {Function} middleware | ||
* @api public | ||
*/ | ||
Passport.prototype.authenticate = function(strategy, options, callback) { | ||
return authenticate(strategy, options, callback).bind(this); | ||
} | ||
/** | ||
* Registers a function used to serialize user objects into the session. | ||
* | ||
* Examples: | ||
* | ||
* passport.serializeUser(function(user, done) { | ||
* done(null, user.id); | ||
* }); | ||
* | ||
* @api public | ||
*/ | ||
Passport.prototype.serializeUser = function(fn, done) { | ||
if (typeof fn === 'function') { | ||
return this._serializers.push(fn); | ||
} | ||
// private implementation that traverses the chain of serializers, attempting | ||
// to serialize a user | ||
var user = fn; | ||
var stack = this._serializers; | ||
(function pass(i, err, obj) { | ||
// serializers use 'pass' as an error to skip processing | ||
if ('pass' === err) { | ||
err = undefined; | ||
} | ||
// an error or serialized object was obtained, done | ||
if (err || obj) { return done(err, obj); } | ||
(function pass(i, err) { | ||
if (err) { return next(err); } | ||
var layer = stack[i]; | ||
if (!layer) { return next(); } | ||
try { | ||
layer(req, res, function(e) { pass(i + 1, e); } ) | ||
} catch (e) { | ||
next(e); | ||
} | ||
})(0); | ||
var layer = stack[i]; | ||
if (!layer) { | ||
return done(new Error('failed to serialize user into session')); | ||
} | ||
try { | ||
layer(user, function(e, o) { pass(i + 1, e, o); } ) | ||
} catch(e) { | ||
return done(e); | ||
} | ||
})(0); | ||
} | ||
/** | ||
* Registers a function used to deserialize user objects out of the session. | ||
* | ||
* Examples: | ||
* | ||
* passport.deserializeUser(function(id, done) { | ||
* User.findById(id, function (err, user) { | ||
* done(err, user); | ||
* }); | ||
* }); | ||
* | ||
* @api public | ||
*/ | ||
Passport.prototype.deserializeUser = function(fn, done) { | ||
if (typeof fn === 'function') { | ||
return this._deserializers.push(fn); | ||
} | ||
}; | ||
// private implementation that traverses the chain of deserializers, | ||
// attempting to deserialize a user | ||
var obj = fn; | ||
var stack = this._deserializers; | ||
(function pass(i, err, user) { | ||
// deserializers use 'pass' as an error to skip processing | ||
if ('pass' === err) { | ||
err = undefined; | ||
} | ||
// an error or deserialized user was obtained, done | ||
if (err || user) { return done(err, user); } | ||
var layer = stack[i]; | ||
if (!layer) { | ||
return done(new Error('failed to deserialize user out of session')); | ||
} | ||
try { | ||
layer(obj, function(e, u) { pass(i + 1, e, u); } ) | ||
} catch(e) { | ||
return done(e); | ||
} | ||
})(0); | ||
} | ||
/** | ||
* Return strategy with given `name`. | ||
* | ||
* @param {String} name | ||
* @return {Strategy} | ||
* @api private | ||
*/ | ||
Passport.prototype._strategy = function(name) { | ||
return this._strategies[name]; | ||
} | ||
@@ -69,3 +248,3 @@ | ||
*/ | ||
exports.version = '0.1.0'; | ||
exports.version = '0.1.1'; | ||
@@ -78,23 +257,13 @@ /** | ||
/** | ||
* Expose Connect middleware. | ||
* Expose strategies. | ||
*/ | ||
exports.authenticate = require('./middleware/authenticate')(exports); | ||
exports.strategies = {}; | ||
exports.strategies.SessionStrategy = SessionStrategy; | ||
/** | ||
* Auto-load bundled strategies. | ||
* HTTP extensions. | ||
*/ | ||
exports.strategies = {}; | ||
if (path.existsSync(__dirname + '/strategies')) { | ||
fs.readdirSync(__dirname + '/strategies').forEach(function(filename) { | ||
if (/\.js$/.test(filename)) { | ||
var name = filename.substr(0, filename.lastIndexOf('.')); | ||
exports.strategies.__defineGetter__(name, function(){ | ||
return require('./strategies/' + name); | ||
}); | ||
} | ||
}); | ||
} | ||
require('./http/request'); |
@@ -1,9 +0,116 @@ | ||
module.exports = function authenticate(passport) { | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var util = require('util') | ||
, actions = require('../context/http/actions') | ||
, Context = require('../context/http/context') | ||
/** | ||
* Authenticates requests. | ||
* | ||
* Applies the `name`ed strategy (or strategies) to the incoming request, in | ||
* order to authenticate the request. If authentication is successful, the user | ||
* will be logged in and populated at `req.user` and a session will be | ||
* established by default. If authentication fails, an unauthorized response | ||
* will be sent. | ||
* | ||
* Options: | ||
* - `session` Save login state in session, defaults to _true_ | ||
* - `successRedirect` After successful login, redirect to given URL | ||
* - `failureRedirect` After failed login, redirect to given URL | ||
* | ||
* An optional `callback` can be supplied to allow the application to overrride | ||
* the default manner in which authentication attempts are handled. The | ||
* callback has the following signature, where `user` will be set to the | ||
* authenticated user on a successful authentication attempt, or `false` | ||
* otherwise. An optional `profile` will be provided, containing | ||
* service-specific profile information, when using third-party authentication | ||
* strategies. | ||
* | ||
* passport.authenticate('local', function(err, user, profile) { | ||
* if (err) { return next(err) } | ||
* if (!user) { return res.redirect('/signin') } | ||
* res.redirect('/account'); | ||
* }); | ||
* | ||
* Note that if a callback is supplied, it becomes the application's | ||
* responsibility to log-in the user, establish a session, and otherwise perform | ||
* the desired operations. | ||
* | ||
* Examples: | ||
* | ||
* passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }); | ||
* | ||
* passport.authenticate('basic', { session: false }); | ||
* | ||
* passport.authenticate('twitter'); | ||
* | ||
* @param {String} name | ||
* @param {Object} options | ||
* @param {Function} callback | ||
* @return {Function} | ||
* @api public | ||
*/ | ||
module.exports = function authenticate(name, options, callback) { | ||
if (!callback && typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
options = options || {}; | ||
// TODO: Implement support for authenticting with multiple strategies. | ||
return function authenticate(req, res, next) { | ||
// TODO: Allow the strategy (or strategies) to be selected by name. | ||
var s = passport._strategies[0]; | ||
s._handle(req, res, next); | ||
next(); | ||
var passport = this; | ||
var delegate = {}; | ||
delegate.success = function(user, profile) { | ||
if (callback) { | ||
return callback(null, user, profile); | ||
} | ||
req.logIn(user, options, function(err) { | ||
if (err) { return next(err); } | ||
if (options.successRedirect) { | ||
return res.redirect(options.successRedirect); | ||
} | ||
next(); | ||
}); | ||
} | ||
delegate.fail = function() { | ||
if (callback) { | ||
return callback(null, false); | ||
} else if (options.failureRedirect) { | ||
return res.redirect(options.failureRedirect); | ||
} | ||
// TODO: Set WWW-Authenticate header with challenge | ||
//this.res.setHeader('WWW-Authenticate', 'Basic realm="WWW"'); | ||
// When failure handling is not delegated to the application, the default | ||
// is to respond with 401 Unauthorized. Note that the WWW-Authenticate | ||
// header will be set according to the strategies in use (see | ||
// actions#fail). | ||
res.statusCode = 401; | ||
res.end('Unauthorized'); | ||
} | ||
// 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(name); | ||
if (!prototype) { return next(new Error('no strategy registered under name: ' + name)); } | ||
var strategy = Object.create(prototype); | ||
var context = new Context(delegate, req, res, next); | ||
augment(strategy, actions, context); | ||
strategy.authenticate(req); | ||
} | ||
} | ||
function augment(strategy, actions, ctx) { | ||
for (var method in actions) { | ||
strategy[method] = actions[method].bind(ctx); | ||
} | ||
} |
@@ -12,9 +12,16 @@ /** | ||
*/ | ||
function Strategy(options) { | ||
options = options || {}; | ||
this.name = options.name; | ||
function Strategy() { | ||
} | ||
Strategy.prototype._handle = function(req, res, next) { | ||
this.authenticate(req, res, next); | ||
/** | ||
* Authenticate request. | ||
* | ||
* This function must be overridden by subclasses. In abstract form, it always | ||
* throws an exception. | ||
* | ||
* @param {Object} req | ||
* @api protected | ||
*/ | ||
Strategy.prototype.authenticate = function(req) { | ||
throw new Error('Strategy#authenticate must be overridden by subclass'); | ||
} | ||
@@ -21,0 +28,0 @@ |
{ | ||
"name": "passport", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Authentication framework for Connect and Express.", | ||
@@ -5,0 +5,0 @@ "author": "Jared Hanson <jaredhanson@gmail.com> (http://www.jaredhanson.net/)", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
20069
11
643
1
2