Adding the Passport middleware

As mentioned earlier, we use Passport to deal with user authentication in our API. Here, we will see how to use and store sessions and encrypt a user password to maintain a secure authentication.

First of all, let's install and save the Passport middleware to the application:

  1. Open the terminal and type the following command:
    npm install passport passport-local --save
    
  2. Place the following code after the app express variable:
    // Passport configuration
    require('./server/config/passport')(passport);
  3. Now, we need to create a passport.js file and the necessary code inside the config folder. We can name this file with any name. However, to demonstrate the use of the passport module, we use the same name from the module. Create a passport.js file in the config folder and place the following code:
    // Import passport module
    var LocalStrategy = require('passport-local').Strategy;
    
    // Import the user model
    var User = require('../../server/models/user'),
    
    module.exports = function(passport) {
      // passport setup
      // serialize user
      passport.serializeUser(function(user, done) {
        done(null, user.id);
        });
      // deserialize user
      passport.deserializeUser(function(id, done) {
        User.findById(id, function(err, user) {
          done(err, user);
          });
      });
      // Configure local login strategy
      passport.use('local-login', new LocalStrategy({
        // change default username and password, to email andpassword
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true
      },
      function(req, email, password, done) {
        if (email) {
          // format to lower-case
          email = email.toLowerCase();
        }
      // asynchronous
      process.nextTick(function() {
        User.findOne({ 'local.email' :  email }, function(err,user) {
           // if errors
           if (err) {
             return done(err);
           }
        // check errors and bring the messages
          if (!user) {
            // third parameter is a flash warning message
            return done(null, false, req.flash('loginMessage','No user found.'));
          }
          if (!user.validPassword(password)) {
            return done(null, false, req.flash('loginMessage','Warning! Wrong password.'));
          } else {
            // everything ok, get user
            return done(null, user);
          }
        });
      });
      }));
      // Configure signup local strategy
      passport.use('local-signup', new LocalStrategy({
        // change default username and password, to email andpassword
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true
      },
      function(req, email, password, done) {
        if (email) {
          // format to lower-case
          email = email.toLowerCase();
        }
      // asynchronous
      process.nextTick(function() {
        // if the user is not already logged in:
          if (!req.user) {
            User.findOne({ 'local.email' :  email },function(err, user) {
              // if errors
              if (err) {
                return done(err);
              }
            // check email
            if (user) {
              return done(null, false,req.flash('signupMessage','Warning! the email isalready taken.'));
            } else {
               // create the user
               var newUser = new User();
               newUser.local.email = email;
               newUser.local.password =newUser.generateHash(password);
               newUser.save(function(err) {
                 if (err) {
                   throw err;
                 }
                 return done(null, newUser);
            });
            }
          });
        } else {
          // everything ok, register user
          return done(null, req.user);
        }
      });
      }));
    };

We wrote a basic configuration for using Passport. You can find more information regarding this process at http://passportjs.org/guide/configure/ and http://passportjs.org/guide/username-password/.

Note that we implemented a warning message using the connect-flash module. This is a simple module to show warning messages to a user. The flash messages are stored in the session.

Using this module, we can easily show messages just using the req.flash() function, as we used in the previous code, by following these steps:

  1. Open your terminal and type the following command:
    npm install connect-flash --save
    
  2. Place the flash module into the server.js file after the Mongoose variable with the following code:
    var flash = require('connect-flash'),
  3. Add the following highlighted code to the app.use() function:
    ....
    app.use('/api/speakers', speakers);
    // flash warning messages
    app.use(flash()); 
    
  4. We have flash messages that are ready to use. To finish the Passport configuration, we need to import the Passport module and add to the app.use() function in the server.js file.
  5. Add the following code to the server.js file, after the flash variable:
    var flash = require('connect-flash'),
    var passport = require('passport'),
    
  6. Add the following highlighted code to the app.use() function:
    // flash warning messages
    app.use(flash());
    // Init passport authentication
    app.use(passport.initialize());
    // persistent login sessions
    app.use(passport.session());
    

After performing these steps, we now have the baseline to use Passport, but we still need to perform some more steps. Let's go ahead and add session control and password encryption.

Adding session control and password encryption

To take care of adding session control and password encryption, two important tasks, we will use two very useful middleware: express-session, which as the name suggests controls the user session, and connect-mongo to store the user session on MongoDB.

  1. Open your terminal and type the following command:
    npm install express-session connect-mongo --save
    
  2. Add the module to the server.js file with the following highlighted code:
    var passport = require('passport'),
    // Modules to store session
    var session = require('express-session'),
    var MongoStore = require('connect-mongo')(session);
    
  3. Add the following highlighted code to the app.use() function:
    app.use(passport.session());
    // required for passport
    // secret for session
    app.use(session({
      secret: 'sometextgohere',
      saveUninitialized: true,
      resave: true,
      //store session on MongoDB using express-session + connectmongo
      store: new MongoStore({
        url: config.url,
        collection : 'sessions'
        })
    }));
    

    Note that, besides using the session, here we determined that MongoDB stores the user session in a collection called sessions.

    Tip

    You can find more information about express-session at https://github.com/expressjs/session.

  4. Now, let's add the bcrypt-nodejs module to deal with password encryption. The first step is to install the module. Open your terminal and type:
    npm install bcrypt-nodejs --save
    

Password encryption is a security measure widely adopted in web applications, and the Node.js ecosystem gives us this great alternative.

You can find more about bcrypt for Node.js applications at https://github.com/shaneGirish/bcrypt-nodejs.

In the next section, we'll see how to use this middleware on a user model.

Setting password encryption to a user model

Let's continue to use the bcrypt module and implement it in our user model.

First, let's create the file where we will use this module. Do not yet perform any change in the server.js file as we did previously with other modules; this is due to the fact that we will use it only in the user model.

  1. Add a new file called user.js to the models folder.
  2. Now, let's create a Mongoose schema to hold the user password and e-mail. Place the following code in the user.js file:
    // Import mongoose and bcrypt
    var mongoose = require('mongoose'),
    var bcrypt = require('bcrypt-nodejs'),
    
    var Schema = mongoose.Schema;
    
    // define the schema for our user model
    var userSchema = new Schema({
    
      local: {
      email: String,
      password: String,
      }
    });
    
    // generating a hash
    userSchema.methods.generateHash = function(password) {
      return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
    };
    
    // validating if password is valid
    userSchema.methods.validPassword = function(password) {
      return bcrypt.compareSync(password, this.local.password);
    };
    
    // create the model for users and export to app
    module.exports = mongoose.model('User', userSchema); 

Note that we use bcrypt to generate a hash for the password in the highlighted code and also a validate method (methods.validPassword) to check whether the password is valid.

Reviewing the changes in the server.js file

It's time to revise all changes made in the server.js file, just to check whether something is missing. The highlighted lines of code indicate that they were added in this stage.

Your file should look like the following code:

// Import the Modules / assign modules to variables
var express = require('express'),
var path = require('path'),
var favicon = require('static-favicon'),
var logger = require('morgan'),
var cookieParser = require('cookie-parser'),
var bodyParser = require('body-parser'),
var mongoose = require('mongoose'),
var flash = require('connect-flash'),
var passport = require('passport'),

// Modules to store session
var session = require('express-session'),
var MongoStore = require('connect-mongo')(session);

// Setup Routes
var routes = require('./server/routes/index'),
var users = require('./server/routes/users'),
var speakers = require('./server/routes/speakers'),

// Database configuration
var config = require('./server/config/config.js'),
// connect to our database
mongoose.connect(config.url);
// Check if MongoDB is running
mongoose.connection.on('error', function() {
  console.error('MongoDB Connection Error. Make sure MongoDB isrunning.'),
});

var app = express();

// Passport configuration
require('./server/config/passport')(passport);

// view engine setup
app.set('views', path.join(__dirname, 'server/views'));
app.set('view engine', 'ejs'),

app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// required for passport
// secret for session
app.use(session({
  secret: 'sometextgohere',
  saveUninitialized: true,
  resave: true,
  //store session on MongoDB using express-session + connect mongostore: new MongoStore({
    url: config.url,
    collection : 'sessions'
    })
}));

// flash warning messages
app.use(flash());
// Init passport authentication
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());

// using routes
app.use('/', routes);
app.use('/users', users);
app.use('/api/speakers', speakers);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found'),
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') == 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
     });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() {
  console.log('Express server listening on port ' +server.address().port);
});
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.44.53