Race condition between express-session and Passport.js

Viewed 267

I'm trying to implement the local-strategy for passport.js while using express-session and express-mysql-session. Everything is working just fine, but sometimes after trying to log in from the browser the page keeps hanging until I manually hit refresh. After hitting refresh the app redirects to the user route and the user is logged in correctly, so it's working like it's supposed to, but I had to refresh the page manually.

After searching for a solution I found out what the problem is: there's a race condition between the express-session module and Passport. Apparently, it happens while the session is being stored in the database. Passport doesn't wait and sometimes the redirect is executed before it should, so the browser just hangs.

I found a solution/workaround here, https://github.com/jaredhanson/passport/issues/306#issuecomment-75520619, but I'm still getting the same error. Even if I use req.session.save(cb) and call the redirect inside the callback the race condition happens. The most frustrating part of this is that the race condition happens maybe one out of twenty times, so I have to log in a lot of times to see if the problem is still there or not.

This is my code:

app.use(
    session({
        secret: process.env.SECRET_KEY,
        store: sessionStore,
        resave: false,
        saveUninitialized: false,
        cookie: {
          maxAge: 24 * 60 * 60 * 1000, // 24 hours
        },
    })
);

passport.use(new LocalStrategy( {
    usernameField: 'username_login',
    passwordField: 'user_password_login'
  },
  function(username, password, cb) {
      
      connection.query("SELECT * FROM users WHERE username = ? AND user_password = ?", [username, password], function(err, results) {
        if(err) {
          return cb(err);
        }
        // User doesn’t exist
        if(!results.length) {
          return cb(null, false);
        }

        // Wrong password
        if(results[0].user_password != password) {
          return cb(null, false);
        }

        // Correct username
        return cb(null, results[0]);
      })
}));

passport.serializeUser(function(user, cb) {
  cb(null, user.user_id);
});

passport.deserializeUser(function(id, cb) {
  connection.query("SELECT * FROM users WHERE user_id = ?", [id], function(err, results) {
    if(err) {
      return cb(err);
    }

    cb(null, results[0]);
  })
});

app.use(passport.initialize());
app.use(passport.session());

app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
      if (err || !user) { 
        console.log('Algun error');
        return res.redirect('/login');
      }
      // This is where the problem happens
      req.login(user, function() {
        // Manually save session before redirect. See bug https://github.com/expressjs/session/pull/69
        req.session.save(function(){
          res.redirect('/user');
        });
      });
  })(req, res, next)
});

What am I doing wrong?

0 Answers
Related