Search code examples
node.jsexpresspassport.jsldapauth

How do I properly use sessions with passport_ldapauth?


I've gone through the documentation and have an expressjs app working with login and logout endpoints using passport_ldapauth and sessions. The session is stored to a database using sequelize and a cookie containing just the sessionID, expiration, path, & domain are stored to the browser.

I'm now trying to secure my other endpoints. In the console, I can see that the deserializer is hit first, and the proper query is sent to the database's session table, looking up my user by the proper session ID and finding it.

But then - passport_ldapauth is called and since there is no password, I get a flash message of "Missing credentials" and a 400 return.

I think that is what kav was referring to in his second comment here (https://github.com/vesse/passport-ldapauth/issues/53) which didn't get an answer.

Perhaps I'm complicating this? Should I simply switch to a 'session' strategy (if one exists) instead of 'ldapauth' once I have the user logged in for my other routes? Or is this supposed to work and isn't? Or am I just doing something wrong?

Here is the route in question:

api.post('/role/add', passport.authenticate(
    'ldapauth', {session: true, failureFlash: true}
  ), (req, res, next) => {
    Roles.create(req.body).then(
      role => res.json(role)
    ).catch(err => {
      if (err) {
        console.log(err);
        next(err);
      }
    });
  });

What else might one need me to provide in order to assist me?

Perhaps my serializer or deserializer are bad? They seem to work for the login/logout endpoints. See below:

passport.serializeUser(function(user, done) {
    let sessionUser = {
      username: user.sAMAccountName,
      sessionID: user.sessionID
    };
    done(null, sessionUser);
  });
  
passport.deserializeUser(function(user, done) {
    Session.findOne({where: {sid: user.sessionID}}).then(user => {
      return done(null, user);
    });
  });

Note that the user object returned from Active Directory is ENORMOUS, which is why I'm rewriting the whole thing as a minimum of username and sessionID. I am not sure, but I think the user as returned by the findOne may or may not be the 'passport' object contained in the 'data' column of the database. Hmm. Do I need to ensure that it is? It's possible the user is the whole row from the DB.


Solution

  • Well that was ridiculously easy, but nowhere in the official docs could I find this. So for those who are having difficulty groking passport, here is what I found.

    req.isAuthenticated()

    Simply write your own middleware function, either above your routes or in a separate file, like this:

    const unAuthMsg = 'You are not authorized for this endpoint.';
    
    export const ensureAuthenticated = (req, res, next) => {
      if (req.isAuthenticated()) {
        next();
      } else {
        res.status(401).json({"message": unAuthMsg});
      }
    };

    Then use it in your routes like this:

    api.post('/role/add', ensureAuthenticated, (req, res, next) => {
    Roles.create(req.body).then(role => {
      return res.json(role)
    }).catch(err => {
      if (err) {
        console.log(err);
        next(err);
      }
    });
      });

    And that's it.