1
votes

I am learning javascript and node.js. As you know, one of the most important part of any node app is login module, so I started to play with passport and passport-local but I am not able to understand how exactly passport authenticates. My understanding of passport authentication process is below with the code:

'use strict';

var express = require('express');
var app = express();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var dbConfig = require('./settings/db.js');
var mongoose = require('mongoose');
var expressSession = require('express-session');
var flash = require('connect-flash');

mongoose.connect(dbConfig.url);

app.use(expressSession({
  secret: 'mySecretKey'
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

var server = app.listen(3000, function() {
  var host = server.address().address;
  var port = server.address().port;

  console.log('Admin app started at: %s %s ', host, port);
});

passport.serializeUser(function(user, done) {
  console.log('serializing user!');
  done(null, 'Hi');
});

passport.deserializeUser(function(id, done) {
  console.log('deserializing user');
  done(null, {
    '_id': 'Hi',
    'username': 'shankhs',
    'password': 'admin'
  });
});

var isAuthenticated = function(req, res, next) {
  if (req.isAuthenticated()) {
    console.log('Authenticated');
    console.log(req);
    next();
  }
  console.log('redirecting to /');
  console.log(req.isAuthenticated());
  res.redirect('/');
};

app.get('/', function(req, res, next) {
  var fileOptions = {
    root: __dirname,
    dotfiles: 'deny',
    headers: {
      'x-timestamp': Date.now(),
      'x-sent': true
    }
  };
  res.sendFile('login.html', fileOptions, function(err) {
    if (err) {
      console.log(err);
      res.status(err.status).end();
    } else {
      console.log('send login.html!' + Date.now());
    }
  });
});

app.get('/admin', isAuthenticated, function(req, res, next) {
  var fileOptions = {
    root: __dirname,
    headers: {
      'x-timestamp': Date.now(),
      'x-sent': true
    }
  };
  var fileName = 'index.html';
  res.sendFile(fileName, fileOptions, function(err) {
    if (err) {
      console.log(err);
      res.status(err.status).end();
    } else {
      console.log('Send index.html' + Date.now());
    }
  });
});

passport.use('login', new LocalStrategy(
  function(req, username, password, done) {
    console.log('using passport!');
    console.log(req.body.username);
    console.log(req.body.password);
    done(null, {
      '_id': 'Hi',
      'username': 'shankhs',
      'password': 'admin'
    });
  }
));

app.post('/login', function(req, res) {
  console.log(req.params);
  passport.authenticate('login', {
    successRedirect: '/admin',
    failureRedirect: '/',
    failureFlash: true
  })
});
  1. A post request to /login route invokes async passport.authenticate call.
  2. This passport.authenticate takes a "strategy" as an argument
  3. This strategy is invoked which returns another 'done' async call with (in my case) no error and user object
  4. After the 'done' call, serializeUser is called and the page is redirected to /admin
  5. If there is any subsequent request or any url is called which has isAuthenticated hook, passport.initialize checks whether req.passport.user object is empty or not.
  6. If its empty, the authentication process is again repeated.
  7. If its not, passport.session calls passport.deserializeUser which creates the req.user object

My problem is as follows:

  1. In the 'login' strategy, these three are never logged:

    console.log('using passport!'); console.log(req.body.username); console.log(req.body.password);

So does this mean my 'login' strategy is never called?

  1. Similarly, console.logs in serializeUser and deserializeUser is never called. So these functions are also not getting called?

If my understanding of the of passport library is correct, am I missing any function to call?

Thanks

1

1 Answers

5
votes

For the future warriors:

That is how passport is supposed to work. Detailed explanation on how passport works is here

The thing that is missing here is the bodyparser in the post call to /login. You might think passport knows how to parse req.body! Wrong! It doesn't! Express v 4 onwards body-parser has to be installed independetly and used as :

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: true
}));

In the post request:

app.post('/login', function(req, res, next) {
  console.log('in login post!');
  console.log('body parsing: ' + req.body.username + ' ' + req.body.password);
  passport.authenticate('login', function(err, user, info) {
    if (err) {
      console.log('passport err: ' + err);
      return next(err);
    }
    if (!user) {
      console.log('no user found!');
      return res.redirect('/');
    }
    req.logIn(user, function(err) {
      if (err) {
        console.log('login error: ' + err);
        return next(err);
      }
      return res.redirect('/admin');
    });
  })(req, res, next);
});

As you can see, I wrote a custom logIn callback function, which makes more sense and easily debuggable than just successRedirect and failureRedirect. Without the body-parser the custom function wont work because passport doesnt know how to parse req.body.

The api doc of passport should mention this! Well, now since everything works as intended, the world makes sense now!