Building on the tutorial from http://cwbuecheler.com/web/tutorials/2013/node-express-mongo/ I wanted to add passport support to allow basic authentication to the site and there seemed to be a few too many conflicting and out of date instructions for what should have been a really simple process.
The objective is local username/password authentication and as we are doing that we might as well be good and store a password hash not the actual password in the db. This could easily be extended to be properly salted etc.
The extra modules we are using that need to be added to package.json are
passport": "*", "passport-local": "*", "crypto": "*", "connect-flash": "*"
then of cause you need to issue a
npm --install
to drag in those extra modules
back in app.js we need to add the following at the top to require the correct modules
Right at the top before you require express
var flash = require('connect-flash'); //flash now should be before express and passport
then you also want to add at the end of the require list
var crypto = require('crypto'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy;
Passport setup
To use basic local username passwords with passport you need the following code, the bits that were missing from many examples were the passport.serializeUser() and passport.deserializeUser() functions. In our setup we also reused the usercollection database in mongo called users that contains username and added a password hash and we are using SHA1 as the hash function (passport supports other hashes as well). This particular example takes the password hashes it then checks against the hash stored in the database. An improvement would be to send the hash in the post request instead of the plain text.
If you want to add a user to the mongo db then using the mongo console
use nodetest1 db.usercollection.insert({ "username" : "testuser1", "email" : "testuser1@testdomain.com", "hash" : "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"})
NB the hash is for the password test, so when we come to test this later username testuser1 password test
And this is the extra app.js code to setup passport.
var Users = db.get('usercollection'); passport.use(new LocalStrategy(function(username, password,done){ Users.findOne({ username : username},function(err,user){ if(err) { return done(err); } if(!user){ return done(null, false, { message: 'Incorrect username.' }); } var hash = crypto.createHash('sha1'); hash.update(password); var result = hash.digest('hex') ; if (result == user.hash) { return done(null, user._id); } else { done(null, false, { message: 'Incorrect password.' }); } }); })); passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(id, done) { Users.findById(id, function(err,user){ if(err){done(err);} done(null,user); }); });
Now to hook in the passport to the flow in the app.use section before app.use(app.router); add
app.use(flash()); // this must be before passport setup app.use(passport.initialize()); app.use(passport.session());
Ok cool passport is basically hooked in now so the remaining things are we need a username/password form a way to handle the post request when submitting that form and to prevent unauthorised access to special logged in only user pages. In this example we restrict access to /helloworld and add a login page called /login
function ensureAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/login') } app.get('/helloworld', ensureAuthenticated, routes.helloworld); app.get('/login', routes.login); app.post('/login', passport.authenticate('local', { successRedirect: '/helloworld', failureRedirect: '/login', failureFlash: true }) );
Adding the ensureAuthenticated will ensure the user is authenticated or redirect to the login page. So thats app.js complete now to add the routes in routes/index.js
All we need to add here is a handler to show the login form view
exports.login = function(req, res){ res.render('login', { title: 'Login' }); };
Now to add the view in views/login.jade
extends layout block content h1= title form#formlogin(name="adduser",method="post",action="/login") p span(class=['input']) Username: input#inputName(type="text", placeholder="username", name="username") p span(class=['input']) Password: input#inputName(type="password", placeholder="password", name="password") button#btnSubmit(type="submit") submit
passport expects username and password to be sent in fields called “username” and “password” and these get hooked through to the passport.use(new LocalStrategy(function(username, password,done){} back in app.js
Leave a Reply
You must be logged in to post a comment.