I'm using Node.js, MongoDB, and PassportJS and I'm trying to authenticate using the local strategy (just simple id and password). The problem is that everything in Mongo is asynchronous, but Passport is synchronous. I thought about trying to "hook" passport so that I could create my own callback to continue with authentication once the Mongo callbacks returned, but I don't know how the passport code works (and I'm not yet desperate enough to fire up a debugger and trace it).
The authentication process will actually finish successfully, but not before the app has responded to the client, causing the client to think it is still un-authenticated. For example, after the client tries to authenticate, it is redirected back to the login form because the session isn't authenticated. If I then simply refresh the page, I get in because, by that point, the authentication callbacks have returned.
passport.use(new localAuth(function(username, password, done)
{
process.nextTick(function()
{
mc.connect('mongodb://127.0.0.1:27017/example', function(err, db)
{
if(err)
throw err;
db.collection('users').findOne({GUID: username}, function(err, results)
{
if(err)
throw err;
console.log('Here I am!');
db.close();
return done(null, username);
});
});
});
}));
(I realize the above code is not authenticating anything. I'm just trying to work through the asynchronous approach.)
I've tried multiple different variations on the above, but they all suffer from the same issue: the function given to localAuth()
returns and passport proceeds with authentication before my database lookup has a chance to complete. I've seen several questions on SO about trying to force mongo to work synchronously and the responses are all "don't". I saw this question, which is exactly my scenario and my approach is the same, but I still have the issue of needing to refresh after the callbacks return.
How should I perform authentication in a node.js + mongoDB + passport app?
UPDATE
I have noticed that 'Here I am!' appears in the log during the first attempt (ie. prior to refreshing the page). That makes me think that authentication is finishing and the session is set during the first authentication attempt. Just whatever is being returned from localAuth()
's callback (done()
) isn't making it in time. Then when I refresh, passport tries to get the session and sees that it's there. This brings me back to trying to hook passport. Does anyone know when new localAuth()
gets called during authentication?
UPDATE
function authenticateUser(id, pass, cb)
{
console.log('1');
mc.connect('mongodb://127.0.0.1:27017/sp2010sec', function(err, db)
{
if(err)
throw err;
console.log('2');
db.collection('users').findOne({GUID: id}, function(err, results)
{
if(err)
throw err;
console.log('3');
return cb(err, id);
});
console.log('4');
});
console.log('5');
}
passport.use(new localAuth(function(username, password, done)
{
console.log('6');
authenticateUser(username, password, function(err, user)
{
console.log('7');
return done(err, username);
});
console.log('8');
}));
The above code produces this log:
6
1
5
8
2
4
3
7
Passport does not appear to be waiting for done
to be called.