node.jsexpresspassport.jsldapauth

Why does Passport retry the authentication when it fails to connect?


I have a Node.js & Express app, using Passport and passport-ldapauth for LDAP authentication.

Everything works OK... unless it fails connecting to the LDAP server (it may be offline, or the IP address be wrong, etc.). In that case, passport.authenticate is somehow executed twice (I just can't figure out why), and as a result, I get Error: Can't set headers after they are sent.

These are the relevant parts of my server.js and routes.js:

// Import modules
var express      = require('express');
var session      = require('express-session');
var passport     = require('passport');
var passportLdap = require('passport-ldapauth');

// Configure Express, session suppport, and Passport
var app = express();
app.use(session({ resave: false, saveUninitialized: false, secret: 'foo' }));
passport.use('ldap', new passportLdap({server: { /* LDAP settings */ }}));
app.use(passport.initialize());
app.use(passport.session());

// Routings
app.get('/login', function(req, res) { res.render('loginForm.ejs'); });
app.post('/login', function(req, res) {
    passport.authenticate('ldap', {session: true}, function(err, user, info) {
        if (err || !user) {
            console.log(err ? 'ERROR' : 'CREDENTIALS');
            res.status(403).render('loginForm.ejs', {message: err || 'Wrong credentials!'});
        } else {
            console.log('SUCCESS');
            res.redirect('/home');
        }
    })(req, res);
});

If the authentication is successful, the console only shows SUCCESS.

If the user enters either a wrong username or password, it shows CREDENTIALS, and the login form is loaded again, displaying the "Wrong credentials!" message. No errors appear on the console.

However, if the LDAP server is down, I get ERROR twice on the console. (Why?!) Then obviously, as it's doing a second res.render with the same response object, I get the Can't set headers after they are sent error. And since it's the second attempt that fails, the user still gets the form login with the proper error message (which was rendered on the first attempt).

If I change my if (err) block to do next(err) instead of res.render()... I don't get the headers problem, but still, the exception with the LDAP (for example "Error: getaddrinfo ENOTFOUND") is shown twice on the console, as if Passport was retrying after the first attempt.

Also, I have noticed there is a handleErrorsAsFailures option in ldapauth. If I enable it, then it shows CREDENTIALS twice on the console for connection problems, and only once if I actually input wrong credentials.

What could be causing this issue?


Solution

  • As a (hopefully temporal?) workaround, I've changed my code to this:

    if (err || !user) {
        if (!res.headersSent) { // <---------- CHECK TO PREVENT THE ISSUE ---------
            console.log(err ? 'ERROR' : 'CREDENTIALS');
            res.status(403).render('loginForm.ejs', {message: err || 'Wrong credentials!'});
        }
    } else // ...
    

    Which prevents the second render attempt, and avoids the Can't set headers after they are sent error.

    I still have no clue of what's causing this behaviour or how to fix it, though...