Getting security setup in Express with PassportJS had a slight learning curve to it for me. PassportJS has a variety of strategies that they use to implement security. I needed a custom security provider based on Azure Table Storage. Azure Table Storage is cheap and lightweight, so it was a great fit for a developer on a budget. With Passport the key object to implement is the User object. User implements a few key functions findOne, save, and isValidPassword. I also used the bcrypt package to encrypt the passwords. If you are looking for a more indepth tutorial on how to use PassportJS check out http://scotch.io/tutorials/javascript/easy-node-authentication-setup-and-local
var azure = require("azure-storage"); var q = require('q'); var config = require('../config.js'); var bCrypt = require('bcrypt-nodejs'); function User() { } User.UserFactory = function() { var user = { userName : "", password : "", enabled : true, email : "", validated : false }; user.isValidPassword = User.isValidPassword; return user; } User.findOne = function (userName) { var tableService = azure.createTableService(config.storageAccountKey); var deferred = q.defer(); tableService.retrieveEntity('CloudUser', userName, 'true', function (err, result) { if (err) { if (err.statusCode === 404) deferred.resolve(undefined); else deferred.reject(err); } else { deferred.resolve(User.toUser(result)); } }); return deferred.promise; } User.toUser = function (userEntity) { var user = User.UserFactory(); user.userName = userEntity.PartitionKey._; user.enabled = userEntity.RowKey._; user.password = userEntity.password._; user.email = userEntity.email._; user.validated = userEntity.validated._; return user; }; User.toUserEntity = function (user) { return { PartitionKey: { '_': user.userName }, RowKey: { '_': 'true' }, password: { '_': user.password }, email: { '_': user.email }, validated: { '_': user.validated } }; }; User.createUser = function (user) { var deferred = q.defer(); var tableService = azure.createTableService(config.storageAccountKey); function create(user) { tableService.insertEntity('CloudUser', User.toUserEntity(user), function (err, result) { if (err) { deferred.reject({ status: false, reason: "error creating user", err: err }); } else { deferred.resolve({ status: true, reason: "" }); } }); } User.findOne(user.userName).then(function (result) { if (result && result.length > 1) { deferred.resolve({ status: false, reason: "username exists" }); } else { create(user); } }). fail(function (err) { deferred.reject({ status: false, reason: "error creating user";, err: err}); }); return deferred.promise; } User.save = function (user) { var tableService = azure.createTableService(config.storageAccountKey); tableService.updateEntity('CloudUser', user, function (err, result) { if (err) { deferred.reject({ status: false, reason: "error creating user" }); } else { deferred.resolve({ status: true, reason: "" }); } }); } User.isValidPassword = function (user, password) { return bCrypt.compareSync(password, user.password); } // Generates hash using bCrypt User.createHash = function (password) { return bCrypt.hashSync(password); } module.exports = User;
Now that we a User object that can find, save and validate passwords – we need plug it into a local strategy.
module.exports = function (passport) { passport.use('login', new LocalStrategy({ passReqToCallback : true }, function (req, username, password, done) { // check if a user with username exists or not User.findOne(username).then(function(user){ if (!user) { console.log('User Not Found with username ' + username); return done(null, false, req.flash('message', 'User Not found.')); } // User exists but wrong password, log the error if (!User.isValidPassword(user, password)) { console.log('Invalid Password'); return done(null, false, req.flash('message', 'Invalid Password')); // redirect back to login page } // User and password both match, return user from done method // which will be treated like success return done(null, user); }).fail(function (err) { // In case of any error, return using the done method return done(err); }); }) ); }
Of course we will also need a strategy to sign up new users.
</pre> module.exports = function (passport) { passport.use('signup', new LocalStrategy({ passReqToCallback : true // allows us to pass back the entire request to the callback }, function (req, userName, password, done) { findOrCreateUser = function () { // find a user in Mongo with provided username User.findOne(userName).then(function (user) { // already exists if (user) { console.log('User already exists with username: ' + username); return done(null, false, req.flash('message', 'User Already Exists')); } else { // if there is no user with that email // create the user var newUser = User.UserFactory(); // set the user's local credentials newUser.userName = userName; newUser.password = User.createHash(password); newUser.email = req.param('email'); newUser.firstName = req.param('firstName'); newUser.lastName = req.param('lastName'); // save the user User.createUser(newUser).then(function (err) { console.log('User Registration succesful'); return done(null, newUser); }).fail(function (err) { if (err) { console.log('Error in Saving user: ' + err); throw err; } }); } }).fail(function (err) { // In case of any error, return using the done method if (err) { console.log('Error in SignUp: ' + err); return done(err); } }); }; // Delay the execution of findOrCreateUser and execute the method // in the next tick of the event loop process.nextTick(findOrCreateUser); }) ); }
And finally plug the strategies into Passport with the following code.
// Initialize Passport app.use(passport.initialize()); app.use(passport.session()); // Passport needs to be able to serialize and deserialize users to support persistent login sessions passport.serializeUser(function (user, done) { done(null, user.userName); }); passport.deserializeUser(function (id, done) { User.findOne(id).then(function (user) { done(null, user); }).fail(function (err) { done(err); }); }); // Setting up Passport Strategies for Login and SignUp/Registration login(passport); signup(passport);
You can checkout the code at https://github.com/rocketcoder/passportjs-AzureTableStorge
Reblogged this on Dinesh Ram Kali..