feathers-authentication
Advanced tools
Comparing version 0.7.9 to 0.8.0-alpha
@@ -9,2 +9,4 @@ var feathers = require('feathers'); | ||
var authentication = require('../lib/index'); | ||
var token = authentication.TokenService; | ||
var local = authentication.LocalService; | ||
@@ -21,8 +23,20 @@ // Initialize the application | ||
.configure(authentication({ | ||
idField: 'id' | ||
user: { | ||
idField: 'id' | ||
}, | ||
token: { | ||
secret: 'super secret' | ||
} | ||
})) | ||
.configure(token()) | ||
.configure(local()) | ||
// Initialize a user service | ||
.use('/users', memory()) | ||
// A simple Message service that we can used for testing | ||
.use('/messages', memory()) | ||
.use('/messages', memory({ | ||
paginate: { | ||
default: 5, | ||
max: 25 | ||
} | ||
})) | ||
.use('/', feathers.static(__dirname + '/public')) | ||
@@ -37,12 +51,8 @@ .use(errorHandler()); | ||
messageService.before({ | ||
all: [ | ||
authentication.hooks.verifyToken(), | ||
authentication.hooks.populateUser(), | ||
authentication.hooks.restrictToAuthenticated() | ||
] | ||
}) | ||
all: [authentication.hooks.restrictToAuthenticated()] | ||
}); | ||
var userService = app.service('users'); | ||
// Add a hook to the user service that automatically replaces | ||
// Add a hook to the user service that automatically replaces | ||
// the password with a hash of the password before saving it. | ||
@@ -56,3 +66,4 @@ userService.before({ | ||
email: 'admin@feathersjs.com', | ||
password: 'admin' | ||
password: 'admin', | ||
roles: ['admin'] | ||
}; | ||
@@ -64,4 +75,12 @@ | ||
app.on('login', function(data) { | ||
console.log('User logged in', data); | ||
}); | ||
app.on('logout', function(data) { | ||
console.log('User logged out', data); | ||
}); | ||
app.listen(3030); | ||
console.log('Feathers authentication app started on 127.0.0.1:3030'); |
@@ -22,3 +22,3 @@ // This is what a NodeJS client looks like | ||
console.log(`Successfully authenticated against ${host}!`, result); | ||
app.service('messages').find({}).then(function(data){ | ||
@@ -30,4 +30,10 @@ console.log('messages', data); | ||
app.service('approved-messages').find({}).then(function(data){ | ||
console.log('approvedMessages', data); | ||
}).catch(function(error){ | ||
console.error('Error finding data', error); | ||
}); | ||
}).catch(function(error){ | ||
console.error('Error authenticating!', error); | ||
}); |
@@ -38,3 +38,3 @@ 'use strict'; | ||
app.set('token', response.token); | ||
app.set('user', response.data); | ||
app.set('user', response.user); | ||
@@ -41,0 +41,0 @@ return Promise.resolve(storage.setItem(config.tokenKey, response.token)).then(function () { |
@@ -39,2 +39,4 @@ 'use strict'; | ||
if (password === undefined) { | ||
// If it was an internal call then move along otherwise | ||
// throw an error that password is required. | ||
if (!hook.params.provider) { | ||
@@ -41,0 +43,0 @@ return hook; |
@@ -39,2 +39,14 @@ 'use strict'; | ||
var _verifyOrRestrict = require('./verify-or-restrict'); | ||
var _verifyOrRestrict2 = _interopRequireDefault(_verifyOrRestrict); | ||
var _populateOrRestrict = require('./populate-or-restrict'); | ||
var _populateOrRestrict2 = _interopRequireDefault(_populateOrRestrict); | ||
var _hasRoleOrRestrict = require('./has-role-or-restrict'); | ||
var _hasRoleOrRestrict2 = _interopRequireDefault(_hasRoleOrRestrict); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -50,3 +62,6 @@ | ||
restrictToRoles: _restrictToRoles2.default, | ||
verifyToken: _verifyToken2.default | ||
verifyToken: _verifyToken2.default, | ||
verifyOrRestrict: _verifyOrRestrict2.default, | ||
populateOrRestrict: _populateOrRestrict2.default, | ||
hasRoleOrRestrict: _hasRoleOrRestrict2.default | ||
}; | ||
@@ -53,0 +68,0 @@ |
@@ -11,5 +11,19 @@ 'use strict'; | ||
return function (hook) { | ||
if (hook.params.user) { | ||
var user = hook.params.user; | ||
// If it's an after hook attach the user to the response | ||
if (hook.result) { | ||
hook.result.user = Object.assign({}, user = !user.toJSON ? user : user.toJSON()); | ||
// remove the id field from the root, it already exists inside the user object | ||
delete hook.result[options.idField]; | ||
} | ||
return Promise.resolve(hook); | ||
} | ||
var id = void 0; | ||
options = Object.assign({}, defaults, hook.app.get('auth'), options); | ||
options = Object.assign({}, defaults, hook.app.get('auth').user, options); | ||
@@ -31,3 +45,3 @@ // If it's an after hook grab the id from the result | ||
return new Promise(function (resolve, reject) { | ||
hook.app.service(options.userEndpoint).get(id, {}).then(function (user) { | ||
hook.app.service(options.endpoint).get(id, {}).then(function (user) { | ||
// attach the user to the hook for use in other hooks or services | ||
@@ -38,3 +52,3 @@ hook.params.user = user; | ||
if (hook.result) { | ||
hook.result.data = Object.assign({}, user = !user.toJSON ? user : user.toJSON()); | ||
hook.result.user = Object.assign({}, user = !user.toJSON ? user : user.toJSON()); | ||
@@ -55,3 +69,3 @@ // remove the id field from the root, it already exists inside the user object | ||
var defaults = { | ||
userEndpoint: '/users', | ||
endpoint: '/users', | ||
idField: '_id' | ||
@@ -58,0 +72,0 @@ }; |
@@ -60,3 +60,10 @@ 'use strict'; | ||
if (field === undefined || field.toString() !== id.toString()) { | ||
if (Array.isArray(field)) { | ||
var fieldArray = field.map(function (idValue) { | ||
return idValue.toString(); | ||
}); | ||
if (fieldArray.length === 0 || fieldArray.indexOf(id.toString()) < 0) { | ||
reject(new _feathersErrors2.default.Forbidden('You do not have the permissions to access this.')); | ||
} | ||
} else if (field === undefined || field.toString() !== id.toString()) { | ||
reject(new _feathersErrors2.default.Forbidden('You do not have the permissions to access this.')); | ||
@@ -63,0 +70,0 @@ } |
221
lib/index.js
@@ -6,5 +6,2 @@ 'use strict'; | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; | ||
exports.default = auth; | ||
@@ -16,10 +13,6 @@ | ||
var _path = require('path'); | ||
var _cookieParser = require('cookie-parser'); | ||
var _path2 = _interopRequireDefault(_path); | ||
var _cookieParser2 = _interopRequireDefault(_cookieParser); | ||
var _crypto = require('crypto'); | ||
var _crypto2 = _interopRequireDefault(_crypto); | ||
var _passport = require('passport'); | ||
@@ -29,2 +22,6 @@ | ||
var _lodash = require('lodash.merge'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
var _hooks = require('./hooks'); | ||
@@ -48,3 +45,3 @@ | ||
var middleware = _interopRequireWildcard(_middleware); | ||
var mw = _interopRequireWildcard(_middleware); | ||
@@ -55,27 +52,54 @@ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
function isObject(item) { | ||
return (typeof item === 'undefined' ? 'undefined' : _typeof(item)) === 'object' && !Array.isArray(item) && item !== null; | ||
} | ||
var debug = (0, _debug2.default)('feathers-authentication:main'); | ||
var PROVIDERS = { | ||
token: _token2.default, | ||
local: _local2.default | ||
}; | ||
// Exposed modules | ||
var THIRTY_SECONDS = 30000; // in milliseconds | ||
var ONE_DAY = 60 * 60 * 24 * 1000; // in milliseconds | ||
// Options that apply to any provider | ||
var defaults = { | ||
idField: '_id', | ||
shouldSetupSuccessRoute: true, | ||
shouldSetupFailureRoute: true, | ||
successRedirect: '/auth/success', | ||
failureRedirect: '/auth/failure', | ||
tokenEndpoint: '/auth/token', | ||
localEndpoint: '/auth/local', | ||
userEndpoint: '/users', | ||
header: 'authorization', | ||
cookie: { | ||
name: 'feathers-jwt', | ||
httpOnly: false, | ||
secure: process.env.NODE_ENV === 'production' | ||
header: 'Authorization', | ||
setupMiddleware: true, // optional - to setup middleware yourself set to false. | ||
cookies: { | ||
enable: false, // Set to true to enable all cookies | ||
// Used for redirects where JS can pick up the JWT and | ||
// store it in localStorage (ie. redirect or OAuth) | ||
'feathers-jwt': { // set to false to disable this cookie | ||
httpOnly: false, | ||
maxAge: THIRTY_SECONDS, | ||
secure: process.env.NODE_ENV === 'production' | ||
}, | ||
// Used for server side rendering | ||
'feathers-session': { // set to false to disable this cookie | ||
httpOnly: true, | ||
// maxAge: ONE_DAY, | ||
maxAge: 0, // session cookie | ||
secure: process.env.NODE_ENV === 'production' | ||
} | ||
}, | ||
token: { | ||
name: 'token', // optional | ||
endpoint: '/auth/token', // optional | ||
issuer: 'feathers', // optional | ||
algorithm: 'HS256', // optional | ||
expiresIn: '1d', // optional | ||
secret: null, // required | ||
successRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
failureRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
successHandler: null }, | ||
// optional - a middleware to handle things once authentication succeeds | ||
local: { | ||
endpoint: '/auth/local', // optional | ||
successRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
failureRedirect: null, // optional - no default. If set the default success handler will redirect to location | ||
successHandler: null }, | ||
// optional - a middleware to handle things once authentication succeeds | ||
user: { | ||
endpoint: '/users', // optional | ||
idField: '_id', // optional | ||
usernameField: 'email', // optional | ||
passwordField: 'password', // optional | ||
service: null // optional - no default. an actual service (can be client side service) | ||
} | ||
@@ -91,36 +115,18 @@ }; | ||
// NOTE (EK): Currently we require token based auth so | ||
// if the developer didn't provide a config for our token | ||
// provider then we'll set up a sane default for them. | ||
if (!config.token) { | ||
config.token = { | ||
secret: _crypto2.default.randomBytes(64).toString('base64') | ||
}; | ||
// If cookies are enabled then load our defaults and | ||
// any passed in options | ||
if (config.cookies && config.cookies.enable) { | ||
config.cookies = Object.assign({}, defaults.cookies, config.cookies); | ||
} | ||
// If they didn't pass in a local provider let's set one up | ||
// for them with the default options. | ||
if (config.local === undefined) { | ||
config.local = {}; | ||
} | ||
if (config.cookie) { | ||
config.cookie = Object.assign({}, defaults.cookie, config.cookie); | ||
} | ||
// Merge and flatten options | ||
var authOptions = Object.assign({}, defaults, app.get('auth'), config); | ||
var authOptions = (0, _lodash2.default)(defaults, app.get('auth'), config); | ||
// If a custom success redirect is passed in or it is disabled then we | ||
// won't setup the default route handler. | ||
if (authOptions.successRedirect !== defaults.successRedirect) { | ||
authOptions.shouldSetupSuccessRoute = false; | ||
// NOTE (EK): Currently we require token based auth so | ||
// if the developer didn't provide a config for our token | ||
// provider then we'll set up a sane default for them. | ||
if (!authOptions.token.secret) { | ||
throw new Error('You must provide a token secret in your config via \'auth.token.secret\'.'); | ||
} | ||
// If a custom failure redirect is passed in or it is disabled then we | ||
// won't setup the default route handler. | ||
if (authOptions.failureRedirect !== defaults.failureRedirect) { | ||
authOptions.shouldSetupFailureRoute = false; | ||
} | ||
// Set the options on the app | ||
@@ -130,11 +136,25 @@ app.set('auth', authOptions); | ||
// REST middleware | ||
if (app.rest) { | ||
if (app.rest && authOptions.setupMiddleware) { | ||
debug('registering REST authentication middleware'); | ||
// Be able to parse cookies it that is enabled | ||
if (authOptions.cookies.enable) { | ||
app.use((0, _cookieParser2.default)()); | ||
} | ||
// Expose Express req & res objects to hooks and services | ||
app.use(mw.exposeRequestResponse(authOptions)); | ||
// Parse token from header, cookie, or request objects | ||
app.use(mw.tokenParser(authOptions)); | ||
// Verify and decode a JWT if it is present | ||
app.use(mw.decodeToken(authOptions)); | ||
// Make the Passport user available for REST services. | ||
// app.use( middleware.exposeAuthenticatedUser() ); | ||
// Get the token and expose it to REST services. | ||
app.use(middleware.normalizeAuthToken(authOptions)); | ||
app.use(mw.populateUser(authOptions)); | ||
// Register server side logout middleware | ||
app.use(mw.logout(authOptions)); | ||
} else if (app.rest) { | ||
debug('Not registering REST authentication middleware. Did you disable it on purpose?'); | ||
} | ||
debug('registering passport middleware'); | ||
app.use(_passport2.default.initialize()); | ||
@@ -146,11 +166,15 @@ | ||
// Socket.io middleware | ||
if (app.io) { | ||
if (app.io && authOptions.setupMiddleware) { | ||
debug('registering Socket.io authentication middleware'); | ||
app.io.on('connection', middleware.setupSocketIOAuthentication(app, authOptions)); | ||
app.io.on('connection', mw.setupSocketIOAuthentication(app, authOptions)); | ||
} else if (app.primus) { | ||
debug('Not registering Socket.io authentication middleware. Did you disable it on purpose?'); | ||
} | ||
// Primus middleware | ||
if (app.primus) { | ||
if (app.primus && authOptions.setupMiddleware) { | ||
debug('registering Primus authentication middleware'); | ||
app.primus.on('connection', middleware.setupPrimusAuthentication(app, authOptions)); | ||
app.primus.on('connection', mw.setupPrimusAuthentication(app, authOptions)); | ||
} else if (app.primus) { | ||
debug('Not registering Primus authentication middleware. Did you disable it on purpose?'); | ||
} | ||
@@ -160,62 +184,11 @@ | ||
}; | ||
// Merge all of our options and configure the appropriate service | ||
Object.keys(config).forEach(function (key) { | ||
// Because we are iterating through all the keys we might | ||
// be dealing with a config param and not a provider config | ||
// If that's the case we don't need to merge params and we | ||
// shouldn't try to set up a service for this key. | ||
if (!isObject(config[key]) || key === 'cookie') { | ||
return; | ||
} | ||
// Check to see if the key is a local or token provider | ||
var provider = PROVIDERS[key]; | ||
var providerOptions = config[key]; | ||
// If it's not one of our own providers then determine whether it is oauth1 or oauth2 | ||
if (!provider && isObject(providerOptions)) { | ||
// Check to see if it is an oauth2 provider | ||
if (providerOptions.clientID && providerOptions.clientSecret) { | ||
provider = _oauth2.default; | ||
} | ||
// Check to see if it is an oauth1 provider | ||
else if (providerOptions.consumerKey && providerOptions.consumerSecret) { | ||
throw new Error('Sorry we don\'t support OAuth1 providers right now. Try using a ' + key + ' OAuth2 provider.'); | ||
} | ||
providerOptions = Object.assign({ provider: key, endPoint: '/auth/' + key }, providerOptions); | ||
} | ||
var options = Object.assign({}, authOptions, providerOptions); | ||
app.configure(provider(options)); | ||
}); | ||
// Register error handling middleware for redirecting to support | ||
// redirecting on authentication failure. | ||
app.use(middleware.failedLogin(authOptions)); | ||
// Setup route handler for default success redirect | ||
if (authOptions.shouldSetupSuccessRoute) { | ||
debug('Setting up successRedirect route: ' + authOptions.successRedirect); | ||
app.get(authOptions.successRedirect, function (req, res) { | ||
res.sendFile(_path2.default.resolve(__dirname, 'public', 'auth-success.html')); | ||
}); | ||
} | ||
// Setup route handler for default failure redirect | ||
if (authOptions.shouldSetupFailureRoute) { | ||
debug('Setting up failureRedirect route: ' + authOptions.failureRedirect); | ||
app.get(authOptions.failureRedirect, function (req, res) { | ||
res.sendFile(_path2.default.resolve(__dirname, 'public', 'auth-fail.html')); | ||
}); | ||
} | ||
}; | ||
} | ||
// Exposed Modules | ||
auth.hooks = _hooks2.default; | ||
auth.middleware = mw; | ||
auth.LocalService = _local2.default; | ||
auth.TokenService = _token2.default; | ||
auth.OAuth2Service = _oauth2.default; | ||
module.exports = exports['default']; |
@@ -9,9 +9,50 @@ 'use strict'; | ||
var _express = require('./express'); | ||
var _exposeRequestResponse = require('./express/expose-request-response'); | ||
var _exposeRequestResponse2 = _interopRequireDefault(_exposeRequestResponse); | ||
var _tokenParser = require('./express/token-parser'); | ||
var _tokenParser2 = _interopRequireDefault(_tokenParser); | ||
var _decodeToken = require('./express/decode-token'); | ||
var _decodeToken2 = _interopRequireDefault(_decodeToken); | ||
var _populateUser = require('./express/populate-user'); | ||
var _populateUser2 = _interopRequireDefault(_populateUser); | ||
var _setCookie = require('./express/set-cookie'); | ||
var _setCookie2 = _interopRequireDefault(_setCookie); | ||
var _loginSuccess = require('./express/login-success'); | ||
var _loginSuccess2 = _interopRequireDefault(_loginSuccess); | ||
var _notAuthenticated = require('./express/not-authenticated'); | ||
var _notAuthenticated2 = _interopRequireDefault(_notAuthenticated); | ||
var _restrictToAuthenticated = require('./express/restrict-to-authenticated'); | ||
var _restrictToAuthenticated2 = _interopRequireDefault(_restrictToAuthenticated); | ||
var _logout = require('./express/logout'); | ||
var _logout2 = _interopRequireDefault(_logout); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = { | ||
exposeConnectMiddleware: _express.exposeConnectMiddleware, | ||
normalizeAuthToken: _express.normalizeAuthToken, | ||
successfulLogin: _express.successfulLogin, | ||
failedLogin: _express.failedLogin, | ||
exposeRequestResponse: _exposeRequestResponse2.default, | ||
tokenParser: _tokenParser2.default, | ||
decodeToken: _decodeToken2.default, | ||
populateUser: _populateUser2.default, | ||
setCookie: _setCookie2.default, | ||
successfulLogin: _loginSuccess2.default, | ||
notAuthenticated: _notAuthenticated2.default, | ||
restrictToAuthenticated: _restrictToAuthenticated2.default, | ||
logout: _logout2.default, | ||
setupSocketIOAuthentication: _sockets.setupSocketIOAuthentication, | ||
@@ -18,0 +59,0 @@ setupPrimusAuthentication: _sockets.setupPrimusAuthentication |
@@ -21,3 +21,3 @@ 'use strict'; | ||
function setupSocketHandler(feathersParams, provider, emit, app, options) { | ||
function setupSocketHandler(feathersParams, provider, emit, disconnect, app, options) { | ||
return function (socket) { | ||
@@ -40,2 +40,10 @@ var errorHandler = function errorHandler(error) { | ||
socket.on('authenticate', function (data) { | ||
function successHandler(response) { | ||
feathersParams(socket).token = response.token; | ||
feathersParams(socket).user = response.user; | ||
socket[emit]('authenticated', response); | ||
app[emit]('login', response); | ||
} | ||
// Authenticate the user using token strategy | ||
@@ -51,7 +59,3 @@ if (data.token) { | ||
// convention and pass it as params using sockets. | ||
app.service(options.tokenEndpoint).create({}, params).then(function (response) { | ||
feathersParams(socket).token = response.token; | ||
feathersParams(socket).user = response.data; | ||
socket[emit]('authenticated', response); | ||
}).catch(errorHandler); | ||
app.service(options.token.endpoint).create({}, params).then(successHandler).catch(errorHandler); | ||
} | ||
@@ -67,15 +71,36 @@ // Authenticate the user using local auth strategy | ||
app.service(options.localEndpoint).create(data, _params).then(function (response) { | ||
feathersParams(socket).token = response.token; | ||
feathersParams(socket).user = response.data; | ||
socket[emit]('authenticated', response); | ||
}).catch(errorHandler); | ||
app.service(options.local.endpoint).create(data, _params).then(successHandler).catch(errorHandler); | ||
} | ||
}); | ||
socket.on('logout', function (callback) { | ||
socket.on(disconnect, function () { | ||
debug('Socket disconnected'); | ||
var params = feathersParams(socket); | ||
var token = params.token; | ||
var user = params.user; | ||
app[emit]('logout', { token: token, user: user }); | ||
delete params.token; | ||
delete params.user; | ||
}); | ||
socket.on('logout', function () { | ||
var callback = arguments.length <= 0 || arguments[0] === undefined ? function () {} : arguments[0]; | ||
// TODO (EK): Blacklist token | ||
// TODO (EK): Maybe we need to delete any cookies. | ||
// Can we send the request object this socket is tied to? | ||
debug('Socket log out'); | ||
try { | ||
delete feathersParams(socket).token; | ||
delete feathersParams(socket).user; | ||
var params = feathersParams(socket); | ||
var token = params.token; | ||
var user = params.user; | ||
app[emit]('logout', { token: token, user: user }); | ||
delete params.token; | ||
delete params.user; | ||
} catch (error) { | ||
@@ -98,3 +123,3 @@ debug('There was an error logging out', error); | ||
return socket.feathers; | ||
}, 'socketio', 'emit', app, options); | ||
}, 'socketio', 'emit', 'disconnect', app, options); | ||
} | ||
@@ -109,3 +134,3 @@ | ||
return socket.request.feathers; | ||
}, 'primus', 'send', app, options); | ||
}, 'primus', 'send', 'disconnection', app, options); | ||
} |
@@ -11,17 +11,30 @@ 'use strict'; | ||
exports.default = function (options) { | ||
options = Object.assign({}, defaults, options); | ||
debug('configuring local authentication service with options', options); | ||
return function () { | ||
var app = this; | ||
var authConfig = Object.assign({}, app.get('auth'), options); | ||
var userEndpoint = authConfig.user.endpoint; | ||
// Initialize our service with any options it requires | ||
app.use(options.localEndpoint, _middleware.exposeConnectMiddleware, new Service(options), (0, _middleware.successfulLogin)(options)); | ||
if (authConfig.token === undefined) { | ||
throw new Error('The TokenService needs to be configured before OAuth'); | ||
} | ||
// Get our initialize service to that we can bind hooks | ||
var localService = app.service(options.localEndpoint); | ||
var tokenEndpoint = authConfig.token.endpoint; | ||
// Register our local auth strategy and get it to use the passport callback function | ||
debug('registering passport-local strategy'); | ||
_passport2.default.use(new _passportLocal.Strategy(options, localService.checkCredentials.bind(localService))); | ||
// TODO (EK): Support pulling in a user and token service directly | ||
// in order to talk to remote services. | ||
var _authConfig$user = authConfig.user; | ||
var idField = _authConfig$user.idField; | ||
var passwordField = _authConfig$user.passwordField; | ||
var usernameField = _authConfig$user.usernameField; | ||
options = (0, _lodash2.default)(defaults, authConfig.local, options, { idField: idField, passwordField: passwordField, usernameField: usernameField, userEndpoint: userEndpoint, tokenEndpoint: tokenEndpoint }); | ||
var successHandler = options.successHandler || _middleware.successfulLogin; | ||
debug('configuring local authentication service with options', options); | ||
// Initialize our service with any options it requires | ||
app.use(options.endpoint, new Service(options), (0, _middleware.setCookie)(authConfig), successHandler(options)); | ||
}; | ||
@@ -50,2 +63,6 @@ }; | ||
var _lodash = require('lodash.merge'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -59,2 +76,6 @@ | ||
var defaults = { | ||
endpoint: '/auth/local', | ||
tokenEndpoint: '/auth/local', | ||
userEndpoint: '/users', | ||
idField: '_id', | ||
usernameField: 'email', | ||
@@ -66,2 +87,4 @@ passwordField: 'password', | ||
// successHandler: null //optional - a middleware to call when successfully authenticated | ||
var Service = exports.Service = function () { | ||
@@ -92,3 +115,5 @@ function Service() { | ||
this.app.service(this.options.localEndpoint).buildCredentials(req, username, password) | ||
debug('Checking credentials'); | ||
this.app.service(this.options.endpoint).buildCredentials(req, username, password) | ||
// Look up the user | ||
@@ -106,2 +131,3 @@ .then(function (params) { | ||
debug('User found'); | ||
return user; | ||
@@ -117,2 +143,4 @@ }).then(function (user) { | ||
debug('Verifying password'); | ||
crypto.compare(password, hash, function (error, result) { | ||
@@ -124,2 +152,3 @@ // Handle 500 server error. | ||
debug('Password correct'); | ||
return done(null, result ? user : false); | ||
@@ -150,4 +179,8 @@ }); | ||
debug('User authenticated via local authentication'); | ||
var tokenPayload = _defineProperty({}, options.idField, user[options.idField]); | ||
// Get a new JWT and the associated user from the Auth token service and send it back to the client. | ||
return app.service(options.tokenEndpoint).create(user).then(resolve).catch(reject); | ||
return app.service(options.tokenEndpoint).create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}); | ||
@@ -165,2 +198,6 @@ | ||
// Register our local auth strategy and get it to use the passport callback function | ||
debug('registering passport-local strategy'); | ||
_passport2.default.use(new _passportLocal.Strategy(this.options, this.checkCredentials.bind(this))); | ||
// prevent regular service events from being dispatched | ||
@@ -167,0 +204,0 @@ if (typeof this.filter === 'function') { |
@@ -11,7 +11,2 @@ 'use strict'; | ||
exports.default = function (options) { | ||
options = Object.assign({}, defaults, options); | ||
options.permissions.state = options.permissions.state === undefined ? true : options.permissions.state; | ||
options.permissions.session = options.permissions.session === undefined ? false : options.permissions.session; | ||
if (!options.provider) { | ||
@@ -21,4 +16,4 @@ throw new Error('You need to pass a `provider` for your authentication provider'); | ||
if (!options.endPoint) { | ||
throw new Error('You need to provide an \'endPoint\' for your ' + options.provider + ' provider'); | ||
if (!options.endpoint) { | ||
throw new Error('You need to provide an \'endpoint\' for your ' + options.provider + ' provider'); | ||
} | ||
@@ -30,25 +25,36 @@ | ||
options.callbackURL = options.callbackURL || options.endPoint + '/' + options.callbackSuffix; | ||
if (!options.clientID) { | ||
throw new Error('You need to provide a \'clientID\' for your ' + options.provider + ' provider'); | ||
} | ||
debug('configuring ' + options.provider + ' OAuth2 service with options', options); | ||
if (!options.clientSecret) { | ||
throw new Error('You need to provide a \'clientSecret\' for your ' + options.provider + ' provider'); | ||
} | ||
return function () { | ||
var app = this; | ||
var Strategy = options.strategy; | ||
var TokenStrategy = options.tokenStrategy; | ||
var authConfig = Object.assign({}, app.get('auth'), options); | ||
var userEndpoint = authConfig.user.endpoint; | ||
// Initialize our service with any options it requires | ||
app.use(options.endPoint, _middleware.exposeConnectMiddleware, new Service(options), (0, _middleware.successfulLogin)(options)); | ||
// TODO (EK): Support pulling in a user and token service directly | ||
// in order to talk to remote services. | ||
// Get our initialized service | ||
var service = app.service(options.endPoint); | ||
if (authConfig.token === undefined) { | ||
throw new Error('The TokenService needs to be configured before OAuth'); | ||
} | ||
// Register our Passport auth strategy and get it to use our passport callback function | ||
debug('registering passport-' + options.provider + ' OAuth2 strategy'); | ||
_passport2.default.use(new Strategy(options, service.oauthCallback.bind(service))); | ||
var tokenEndpoint = authConfig.token.endpoint; | ||
if (TokenStrategy) { | ||
debug('registering passport-' + options.provider + '-token OAuth2 strategy'); | ||
_passport2.default.use(new TokenStrategy(options, service.oauthCallback.bind(service))); | ||
} | ||
options = (0, _lodash2.default)(defaults, authConfig[options.provider], options, { userEndpoint: userEndpoint, tokenEndpoint: tokenEndpoint }); | ||
var successHandler = options.successHandler || _middleware.successfulLogin; | ||
options.permissions.state = options.permissions.state === undefined ? true : options.permissions.state; | ||
options.permissions.session = options.permissions.session === undefined ? false : options.permissions.session; | ||
options.callbackURL = options.callbackURL || options.endpoint + '/' + options.callbackSuffix; | ||
debug('configuring ' + options.provider + ' OAuth2 service with options', options); | ||
// Initialize our service with any options it requires | ||
app.use(options.endpoint, new Service(options), (0, _middleware.setCookie)(authConfig), successHandler(options)); | ||
}; | ||
@@ -71,2 +77,6 @@ }; | ||
var _lodash = require('lodash.merge'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -184,4 +194,6 @@ | ||
var tokenPayload = _defineProperty({}, options.idField, user[options.idField]); | ||
// Get a new JWT and the associated user from the Auth token service and send it back to the client. | ||
return app.service(options.tokenEndpoint).create(user).then(resolve).catch(reject); | ||
return app.service(options.tokenEndpoint).create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}); | ||
@@ -218,4 +230,6 @@ | ||
var tokenPayload = _defineProperty({}, options.idField, user[options.idField]); | ||
// Get a new JWT and the associated user from the Auth token service and send it back to the client. | ||
return app.service(options.tokenEndpoint).create(user).then(resolve).catch(reject); | ||
return app.service(options.tokenEndpoint).create(tokenPayload, { user: user }).then(resolve).catch(reject); | ||
}); | ||
@@ -233,2 +247,14 @@ | ||
// Register our Passport auth strategy and get it to use our passport callback function | ||
var Strategy = this.options.strategy; | ||
var TokenStrategy = this.options.tokenStrategy; | ||
debug('registering passport-' + this.options.provider + ' OAuth2 strategy'); | ||
_passport2.default.use(new Strategy(this.options, this.oauthCallback.bind(this))); | ||
if (TokenStrategy) { | ||
debug('registering passport-' + this.options.provider + '-token OAuth2 strategy'); | ||
_passport2.default.use(new TokenStrategy(this.options, this.oauthCallback.bind(this))); | ||
} | ||
// prevent regular service events from being dispatched | ||
@@ -235,0 +261,0 @@ if (typeof this.filter === 'function') { |
@@ -11,33 +11,16 @@ 'use strict'; | ||
exports.default = function (options) { | ||
options = Object.assign({}, defaults, options); | ||
debug('configuring token authentication service with options', options); | ||
return function () { | ||
var app = this; | ||
var authConfig = Object.assign({}, app.get('auth'), options); | ||
var passwordField = authConfig.user.passwordField; | ||
// Initialize our service with any options it requires | ||
app.use(options.tokenEndpoint, new Service(options)); | ||
// Get our initialize service to that we can bind hooks | ||
var tokenService = app.service(options.tokenEndpoint); | ||
options = (0, _lodash2.default)(defaults, authConfig.token, options, { passwordField: passwordField }); | ||
// Set up our before hooks | ||
tokenService.before({ | ||
create: [_verifyToken(options)], | ||
find: [_verifyToken(options)], | ||
get: [_verifyToken(options)] | ||
}); | ||
var successHandler = options.successHandler || _middleware.successfulLogin; | ||
tokenService.after({ | ||
create: [_hooks2.default.populateUser(options), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})], | ||
find: [_hooks2.default.populateUser(options), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})], | ||
get: [_hooks2.default.populateUser(options), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})] | ||
}); | ||
debug('configuring token authentication service with options', options); | ||
// Initialize our service with any options it requires | ||
app.use(options.endpoint, new Service(options), (0, _middleware.setCookie)(authConfig), successHandler(options)); | ||
}; | ||
@@ -66,6 +49,10 @@ }; | ||
var _middleware = require('../middleware'); | ||
var _lodash = require('lodash.merge'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -77,7 +64,11 @@ | ||
var defaults = { | ||
payload: [], | ||
endpoint: '/auth/token', | ||
idField: '_id', | ||
passwordField: 'password', | ||
issuer: 'feathers', | ||
subject: 'auth', | ||
algorithm: 'HS256', | ||
expiresIn: '1d' }; | ||
expiresIn: '1d', // 1 day | ||
payload: [] | ||
}; | ||
@@ -90,3 +81,2 @@ /** | ||
*/ | ||
// 1 day | ||
var _verifyToken = function _verifyToken() { | ||
@@ -105,2 +95,4 @@ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
debug('Verifying token'); | ||
var token = hook.params.token; | ||
@@ -178,18 +170,52 @@ | ||
key: 'create', | ||
value: function create(user) { | ||
var options = this.options; | ||
value: function create(data, params) { | ||
var _Object$assign = Object.assign({}, this.options, params.jwt); | ||
var data = _defineProperty({}, options.idField, user[options.idField]); | ||
var algorithm = _Object$assign.algorithm; | ||
var expiresIn = _Object$assign.expiresIn; | ||
var notBefore = _Object$assign.notBefore; | ||
var audience = _Object$assign.audience; | ||
var issuer = _Object$assign.issuer; | ||
var jwtid = _Object$assign.jwtid; | ||
var subject = _Object$assign.subject; | ||
var noTimestamp = _Object$assign.noTimestamp; | ||
var header = _Object$assign.header; | ||
// const payload = this.options.payload; | ||
// Add any additional payload fields | ||
options.payload.forEach(function (field) { | ||
return data[field] = user[field]; | ||
}); | ||
var secret = this.options.secret; | ||
var options = { | ||
algorithm: algorithm, | ||
expiresIn: expiresIn, | ||
notBefore: notBefore, | ||
audience: audience, | ||
issuer: issuer, | ||
jwtid: jwtid, | ||
subject: subject, | ||
noTimestamp: noTimestamp, | ||
header: header | ||
}; | ||
// const data = { | ||
// [this.options.idField]: payload[this.options.idField] | ||
// }; | ||
// // Add any additional payload fields | ||
// if (payload && Array.isArray(payload)) { | ||
// payload.forEach(field => data[field] = payload[field]); | ||
// } | ||
// Our before hook determined that we had a valid token or that this | ||
// was internally called so let's generate a new token with the user | ||
// id and return both the ID and the token. | ||
return new Promise(function (resolve) { | ||
_jsonwebtoken2.default.sign(data, options.secret, options, function (token) { | ||
return resolve(Object.assign(data, { token: token })); | ||
return new Promise(function (resolve, reject) { | ||
debug('Creating JWT using options:', options); | ||
_jsonwebtoken2.default.sign(data, secret, options, function (error, token) { | ||
if (error) { | ||
debug('Error signing JWT'); | ||
return reject(error); | ||
} | ||
debug('New JWT issued with payload', data); | ||
return resolve({ token: token }); | ||
}); | ||
@@ -201,2 +227,26 @@ }); | ||
value: function setup() { | ||
var options = this.options; | ||
// Set up our before hooks | ||
this.before({ | ||
create: [_verifyToken(options)], | ||
find: [_verifyToken(options)], | ||
get: [_verifyToken(options)] | ||
}); | ||
// TODO (EK): I'm not sure these should be done automatically | ||
// I think this should be left up to the developer or the | ||
// generator. | ||
this.after({ | ||
create: [_hooks2.default.populateUser(), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})], | ||
find: [_hooks2.default.populateUser(), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})], | ||
get: [_hooks2.default.populateUser(), _feathersHooks2.default.remove(options.passwordField, function () { | ||
return true; | ||
})] | ||
}); | ||
// prevent regular service events from being dispatched | ||
@@ -203,0 +253,0 @@ if (typeof this.filter === 'function') { |
{ | ||
"name": "feathers-authentication", | ||
"description": "Add Authentication to your FeathersJS app.", | ||
"version": "0.7.9", | ||
"version": "0.8.0-alpha", | ||
"homepage": "https://github.com/feathersjs/feathers-authentication", | ||
@@ -35,3 +35,4 @@ "main": "lib/", | ||
"release:major": "npm version major && npm publish", | ||
"compile": "rm -rf lib/ && babel -d lib/ src/ && mkdir lib/public/ && cp src/public/* lib/public/", | ||
"release:pre": "npm publish --tag next", | ||
"compile": "rm -rf lib/ && babel -d lib/ src/", | ||
"watch": "babel --watch -d lib/ src/", | ||
@@ -50,7 +51,10 @@ "jshint": "jshint src/. test/. --config", | ||
"bcryptjs": "^2.3.0", | ||
"cookie-parser": "^1.4.3", | ||
"debug": "^2.2.0", | ||
"feathers-errors": "^2.0.1", | ||
"feathers-hooks": "^1.5.0", | ||
"jsonwebtoken": "^5.4.0", | ||
"jsonwebtoken": "^7.1.6", | ||
"lodash.isplainobject": "^4.0.4", | ||
"lodash.merge": "^4.4.0", | ||
"lodash.omit": "^4.3.0", | ||
"passport": "^0.3.0", | ||
@@ -60,3 +64,2 @@ "passport-local": "^1.0.0" | ||
"devDependencies": { | ||
"async": "^1.4.2", | ||
"babel-cli": "^6.1.18", | ||
@@ -78,4 +81,2 @@ "babel-core": "^6.1.21", | ||
"nsp": "^2.2.0", | ||
"passport-facebook": "^2.1.0", | ||
"passport-github": "^1.0.0", | ||
"primus": "^5.0.1", | ||
@@ -87,5 +88,4 @@ "primus-emitter": "^3.1.1", | ||
"socket.io-client": "^1.1.0", | ||
"ws": "^1.0.1", | ||
"xmlhttprequest": "^1.6.0" | ||
"ws": "^1.0.1" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
140104
23
39
2326
0
11
+ Addedcookie-parser@^1.4.3
+ Addedlodash.merge@^4.4.0
+ Addedlodash.omit@^4.3.0
+ Addedcookie@0.7.2(transitive)
+ Addedcookie-parser@1.4.7(transitive)
+ Addedcookie-signature@1.0.6(transitive)
+ Addedhoek@2.16.3(transitive)
+ Addedisemail@1.2.0(transitive)
+ Addedjoi@6.10.1(transitive)
+ Addedjsonwebtoken@7.4.3(transitive)
+ Addedlodash.merge@4.6.2(transitive)
+ Addedlodash.omit@4.5.0(transitive)
+ Addedlodash.once@4.1.1(transitive)
+ Addedmoment@2.30.1(transitive)
+ Addedtopo@1.1.0(transitive)
- Removedjsonwebtoken@5.7.0(transitive)
- Removedms@0.7.3(transitive)
Updatedjsonwebtoken@^7.1.6