Nodejs and the MEAN stack for the .NET developer part 5 – adding auth with passport-local-mongoose

I spent a whole evening figuring out authentication. I also started refactoring my code.

  • I’ve renamed /server/app.js to server.js to distuinguish from the app.js file in the /public/javascripts folder.
  • I’ve renamed the folder ‘javascripts’ to ‘js’
  • I’ve refactored the api.js and now use ‘module.exports’

What does the application need?

I decided to start simple and implement local authentication against MongoDb.
Authentication in Nodejs is handled by the middleware (Express, but it originates from Connect).
There is a fantastic module that handles authentication for you, it’s called Passport.js. Authenticating requests is as simple as calling passport.authenticate() and specifying which strategy to employ (in our case it will be LocalStrategy).

Passport-local-mongoose

Then there is passport-local-mongoose. It adds a Mongoose flavour over Passport, like class inheritance.
It adds extra properties (username and password) to your schema, although you can change ‘username’ to ’email’. It also adds a hash and salt field to store the username, the hashed password and the salt value.

Learn to Read the Source, Luke

I studied the login example back and forth and I actually think I’m grasping it now. The source code never lies. It *is* better than documentation.

Install it like so:

npm-install passport-local passport passport passport-local

Next, rewrite the schema:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');


var memberSchema = new Schema({
  nickname : String,
  email: String,
  firstName: String,
  lastName: String,
//  password: String,
  education: [{ title: String, institute: String, certification: Boolean}]
});

memberSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('Member', memberSchema);

As you see I commented out the password attribute, as it is automatically added through passport-local-mongoose.

This is the new server.js:

var express = require('express');
var http = require('http');
var path = require('path');

var mongoose = require('mongoose');


//!!! added for passport
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var Member = require('./models/Member');


var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.use(express.favicon());
app.use(express.json());
app.use(express.urlencoded());
app.use(express.logger());
app.use(express.methodOverride());
app.use(express.cookieParser());

//!!! added for passport
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());

app.use(app.router);
app.use(express.static(path.join(__dirname, '../public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

//!!! added for passport
passport.use(new LocalStrategy(Member.authenticate()));
passport.serializeUser(Member.serializeUser());
passport.deserializeUser(Member.deserializeUser());


mongoose.connect('mongodb://localhost:27017/local');


//!!! moved the routes to separate file
var api = require('./routes/api')(app);

app.all('/', function(req, res) {
  res.sendfile('index.html', { root: "../public" });
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

I moved the routes to a separate file. The new routes.api looks like this:

var passport = require('passport');
var mongoose = require('mongoose');
var Member = require('../models/Member');

module.exports = function (app) {

//login section
    app.post('/login', passport.authenticate('local'), function(req, res) {
          if(req.user) {    
          res.send(req.user._id);
      }
    });


    app.post('/register', function (req, res) {
      Member.register(new Member({ username : req.body.username }), req.body.password, function(err, member) {
        if (err) {
          return res.send(err);
        }
        res.send(member);       
      });
    });


  //member section
  // /members GET
     app.get('/members',passport.authenticate('local'), function(req,res) {
    Member.find({}, function(err, data) {
      res.send(data);
    });
  });

// /member/id GET
  app.get('/member/:id',passport.authenticate('local'), function(req, res) {
    Member.findOne({ _id : req.params.id }, function(err, data) {
      res.send(data);
    });
  });


 app.put('/member/:id', passport.authenticate('local'), function(req, res) {
    delete req.body._id;
    Member.update({_id: req.params.id}, req.body, function(err, affected) {
      res.send(err);
    });
  });

  app.post('/member',passport.authenticate('local'),function(req,res) {
Member.create(req.body, function(err, member) {
      res.send(member);
    });

  });
}

The biggest gotcha
The login action returns the Mongo ID of the user. Passports saves the user details in req.user.
The id is then returned like so: res.send(req.user._id);

Register a user

auth1

After registering successfully, it is time to log on:

Log on

auth2

In return you get the ID of the logged in user.

3 Replies to “Nodejs and the MEAN stack for the .NET developer part 5 – adding auth with passport-local-mongoose”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: