32

I would like to use passport.js to verify that when users hit certain endpoints that they not only have the correct password but are a member of a specific group or have a certain access.

For simplicity sake if I have access levels of USER and ADMIN.

I can use passport to authenticate a password:

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function(err, user) {
      if (err) { return done(err); }
      if (!user) {
        return done(null, false, { message: 'Incorrect username.' });
      }
      if (!user.validPassword(password)) {
        return done(null, false, { message: 'Incorrect password.' });
      }
      return done(null, user);
    });
  }
));

Then with a route I can make sure the user passes auth:

app.get('/api/users',
  passport.authenticate('local'),
  function(req, res) {
    res.json({ ... });
  });

But lets say you need to have ADMIN acess to hit /api/users'. Do I need to write my own Strategies? IE do I need to have a local-user, and local-admin strategy, and in each verify the proper access levels?

I think I can do this pretty easily, but the problem arises when I need my site to have different auth methods (maybe sometimes use oauth), I would need to write custom *-user, *-admin strategies for each. Seems like overkill.

Other option is to just verify access/group in each route after the user has been authenticated. But I would prefer to do this in the middle-ware if possible.

Thanks

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
lostintranslation
  • 23,756
  • 50
  • 159
  • 262

1 Answers1

54

You could create a simple middleware that checks the group:

var needsGroup = function(group) {
  return function(req, res, next) {
    if (req.user && req.user.group === group)
      next();
    else
      res.send(401, 'Unauthorized');
  };
};

app.get('/api/users', 
  passport.authenticate('local'),
  needsGroup('admin'), 
  function(req, res) {
    ...
  });

This assumes that the object stored in req.user has a property group. This object is the one passed along from the strategy implementation and deserializeUser.

An alternative could be connect-roles, but I don't know how well that integrates with Passport.

EDIT: you could also combine Passport and the group-checking middleware:

var needsGroup = function(group) {
  return [
    passport.authenticate('local'),
    function(req, res, next) {
      if (req.user && req.user.group === group)
        next();
      else
        res.send(401, 'Unauthorized');
    }
  ];
};

app.get('/api/users', needsGroup('admin'), function(req, res) {
});
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Oh, I see. Looks like you can use an array of 'middle-ware' functions that are called in order with express. Did not even realize that, must has missed it. Time to read the manual again ;) Thanks, this is perfect! – lostintranslation Mar 30 '13 at 15:23
  • 2
    Yup, there it is: 'app.VERB(path, [callback...], callback)'. Sorry I missed that and thanks for being so kind with your answer! – lostintranslation Mar 30 '13 at 15:25
  • What I'm looking is an optional authentication: http://stackoverflow.com/questions/25806339/passport-js-optional-authentication – Zlatko Sep 12 '14 at 10:38
  • I know this is super nit picky, but I think a 403 is probably more appropriate of a response in this scenario. Authentication has passed via passport at that point, so really it's an unauthorized request. – Vinay Dec 23 '15 at 08:29
  • my problem is my roles are dynamic from backend api how to handle in that scenario any suggestion ?? – Rizwan Patel Dec 21 '16 at 04:19