UNPKG

@inspire-platform/sails-hook-auth

Version:

Passport-based User Authentication system for sails.js applications.

273 lines (232 loc) 7.37 kB
var crypto = require('crypto'); var base64URL = require('base64url'); var SAError = require('../../../lib/error/SAError.js'); /** * Local Authentication Protocol * * The most widely used way for websites to authenticate users is via a username * and/or email as well as a password. This module provides functions both for * registering entirely new users, assigning passwords to already registered * users and validating login requesting. * * For more information on local authentication in Passport.js, check out: * http://passportjs.org/guide/username-password/ */ /** * @param {Object} req * @param {Object} res * @param {Function} next */ exports.register = function (user, next) { exports.createUser(user, next); }; exports.update = function (user, next) { exports.updateUser(user, next); }; /** * Register a new user * * This method creates a new user from a specified email, username and password * and assign the newly created user a local Passport. * * @param {String} username * @param {String} email * @param {String} password * @param {Function} next */ exports.createUser = function (_user, next) { var accessToken = generateToken(); var password = _user.password; delete _user.password; Passport .checkPasswordStrength(password, _user) .then(() => { return sails.models.user.create(_user, function (err, user) { if (err) { sails.log(err); if (err.code === 'E_VALIDATION') { return next(new SAError({originalError: err})); } return next(err); } sails.models.passport.create({ protocol : 'local' , password : password , user : user.id , accessToken: accessToken }, function (err) { if (err) { if (err.code === 'E_VALIDATION') { err = new SAError({originalError: err}); } return User.destroy( {id: user.id}, function (destroyErr) { next(destroyErr || err); } ); } next(null, user); }); }, {fetch: true}); }) .catch(next); }; /** * Update an user * * This method updates an user based on its id, * and updates the password in local Passport (if provided). * * @param {Object} _user * @param {Function} next */ exports.updateUser = function (_user, next) { var password = _user.password; delete _user.password; if (true === _user.hasOwnProperty('id')) { var userFinder = {id: _user.id}; } else { return next(new Error('A user id is required to update.')); } return sails.models.user.update(userFinder, _user, function (err, user) { if (err) { sails.log(err); if (err.code === 'E_VALIDATION') { return next(new SAError({ originalError: err })); } return next(err); } // Update retrieves an array user = user[0]; // Check if password has a string to replace it if (!!password) { Passport .checkPasswordStrength(password, user) .then(() => { sails.models.passport.findOne({ protocol : 'local' ,user:user.id }, function(err, passport){ Passport .update({id: passport.id}, {password: password}) .exec(function (err) { if (err) { if (err.code === 'E_VALIDATION') { err = new SAError({ originalError: err }); } next(err); } next(null, user); }) }); }) .catch(next); } else { next(null, user); } }, {fetch: true}); }; /** * Assign local Passport to user * * This function can be used to assign a local Passport to a user who doens't * have one already. This would be the case if the user registered using a * third-party service and therefore never set a password. * * @param {Object} req * @param {Object} res * @param {Function} next */ exports.connect = function (req, res, next) { var user = req.user , password = req.param('password') , Passport = sails.models.passport; Passport.findOne({ protocol : 'local' , user : user.id }, function (err, passport) { if (err) { return next(err); } if (!passport) { Passport .checkPasswordStrength(password, user) .then(() => { Passport.create({ protocol: 'local' , password: password , user: user.id }, function (err) { next(err, user); }); }) .catch(next); } else { next(null, user); } }); }; /** * Validate a login request * * Looks up a user using the supplied identifier (email or username) and then * attempts to find a local Passport associated with the user. If a Passport is * found, its password is checked against the password supplied in the form. * * @param {Object} req * @param {string} identifier * @param {string} password * @param {Function} next */ exports.login = function (req, identifier, password, next) { var isEmail = validateEmail(identifier) , query = {}; if (isEmail) { query.email = identifier; } else { query.username = identifier; } sails.services.authservice.findUser(query, function (err, user) { if (err) { return next(err); } if (!user) { return next(null, false); } sails.models.passport.findOne({ protocol : 'local' , user : user.id }, function (err, passport) { if (passport) { Passport.validatePassword(passport, password, function (err, res) { if (err) { return next(err); } if (!res) { return next(null, false); } else { return next(null, user, passport); } }); } else { return next(null, false); } }); }); }; var EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; /** * Use validator module isEmail function * * @see <https://github.com/chriso/validator.js/blob/3.18.0/validator.js#L38> * @see <https://github.com/chriso/validator.js/blob/3.18.0/validator.js#L141-L143> */ function validateEmail (str) { return EMAIL_REGEX.test(str); } function generateToken() { return base64URL(crypto.randomBytes(48)); }