Search code examples
javascriptnode.jsexpresspassport.jspassport-facebook

ExpressJs Passportjs de-serializes user object for every request to a route


I have a ExpressJS app that is using Passportjs to authenticate with Facebook and everything is working as expected exception for one issue.

I have vehicle.js under /routes/ which contains some routes (router.gets and router.posts) that need authentication and some that don't. If user is logged in then every request handled by vehicle.js causes User de-serialization which is a Mongoose lookup. How can I avoid these unnecessary Mongoose lookups when request is made to a router.get and/or router.post that do not need authentication?

I have already looked up this SO question and it does not address my problem (I have declared static resources above passport, so they are not authenticated).

Passport configs in app.js are shown below:

// Configuring Passport
var passport = require('passport');
var expressSession = require('express-session');

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

// Using the flash middleware provided by connect-flash to store messages in session
// and displaying in templates
var flash = require('connect-flash');
app.use(flash());

// Initialize Passport
var initPassport = require('./passport/init');
initPassport(passport);

//passing passport object could be the reason why all requested that are 
//mapped in vehicle cause user de-serialization. Not sure if there is any
//alternative approach than following line that passes passport??
var vehicle = require('./routes/vehicle')(passport);

The following isAuthenticated is in vehicle.js

var isAuthenticated = function (req, res, next) {
  if (req.isAuthenticated())
    return next();
    // if the user is not authenticated then redirect him to the login page
    res.redirect('/vehicle/api/login');
}

Followed by a series of routes that handle logging in, logging out, as well as some actions on vehicle.

module.exports = function(passport) {
  router.get('/api/login', function(req, res) {
    res.render('vehicle/login', { message: req.flash('message') });
  });
  router.post('/api/login', passport.authenticate('login', {
    successRedirect: '/vehicle/api/list/mine',
    failureRedirect: '/vehicle/api/list',
    failureFlash : true
  }));
  ...
  ...
  ...
  router.post('/api/upload', isAuthenticated, function(req, res) {
    //this route needs to  be authenticated, so it works fine, 
    //deserialization done, mongoose lookup, no problem
  }); 
  router.get('/api/image/:vehicleId/:filename', function(req,res) {
    //this route does not need authentication, but causes User 
    //de-serialization and Mongoose lookup
  }); 
 return router;
}

Is it because of the following line that every request to vehicle.js causes User de-serialization when a user is logged in?

var vehicle = require('./routes/vehicle')(passport);

One way to avoid such unnecessary de-serialization would be to separate routes that do not need authentication from vehicle.js to a different file and do not pass that passport object to that file (as it is passed to vehicle.js in app.js). I don't know if that is the correct way to resolve this issue.


Solution

  • You can wrap the passport middleware inside a custom middleware that only invokes it for your specified routes. So Instead of:

    app.use(passport.session());
    

    you could:

    app.use(function(req, res, next){
      if(req.url.match('api/image'))
        next(); // do not invoke passport
      else
        passport.session()(req, res, next)
        // same as doing == app.use(passport.session())
    });