3

The client for my web-app wants to totally separate regular users and admins, hence I'm trying to implement two local strategies for passport.js:

passport.use('local', new LocalStrategy({
    usernameField: 'email'
}, function(email, password, done) {
    User.findOne({ email: email }, function(err, user) {
        if (err) return done(err);
        if (!user) return done(null, false, { message: 'Wrong email or password.' });
        if (!user.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
        done(null, user);
    });
}));

passport.use('admin', new LocalStrategy({
    usernameField: 'email'
}, function(email, password, done) {
    Admin.findOne({ email: email }, function(err, admin) {
        if (err) return done(err);
        if (!admin) return done(null, false, { message: 'Wrong email or password.' });
        if (!admin.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
        done(null, admin);
    });
}));

passport.serializeUser(function(user, done) {
    done(null, user.id);
});

passport.deserializeUser(function(id, done) {

    Admin.findById(id, function(err, admin) {
        if (err) return done(err);
        if (admin) return done(null, admin);
        User.findById(id, function(err, user) {
            done(err, user);
        });
    });
});

And then in my API admin router:

router.post('/', function(req, res, next) {
    if (typeof req.body.email === 'undefined') return res.json({ success: false, message: 'Email not supplied.' });
    if (!validator.isEmail(req.body.email)) return res.json({ success: false, message: 'Wrong email format.' });
    if (typeof req.body.password === 'undefined') return res.json({ success: false, message: 'Password not supplied.' });
    if (req.body.password.length === 0) return res.json({ success: false, message: 'Password not supplied.' });

    passport.authenticate('admin', function(err, admin, info) {
        if (!admin) return res.json({ success: false, message: info.message });
        req.logIn(admin, function(err) {
            if (err) return res.json({ success: false, message: 'Server error.', reason: err });
            console.log('user:');
            console.log(req.user); // both users and admins go here
            console.log('admin:');
            console.log(req.admin); // undefined
            res.json({ success: true });
        });
    })(req, res, next);

});

In fact, I was following the answer here: https://stackoverflow.com/a/21898892/1830420, and Matthew Payne gets two session variables: req.user and req.sponsor. In my case, even if an admin authenticates, it gets written to req.user. Now, trying to implement my client's desire of totally separating users and admins, I want to get req.user and req.admin, but every time it gets written to req.user. I thought changing LocalStrategy name would help, but passport.js seems to ignore both the strategy name and model name.

Any help is very much appreciated.

P.S. I know I can have everyone in User model and write some middleware to protect certain routes based on roles, but then again, unfortunately, I don't get to choose here.

Community
  • 1
  • 1
Anton Egorov
  • 1,328
  • 1
  • 16
  • 33

2 Answers2

2

Unfortunately you can't do that with Passport. You will need to create express middleware to handle admin authentication.

hya
  • 1,708
  • 2
  • 15
  • 22
  • Sigh.. Well, application-wise, it's not a big deal to have both admins and users as User scheme with roles :D Gotta roll back to the role-checking middleware. Thanks! – Anton Egorov Mar 20 '16 at 09:48
  • 1
    no you can http://stackoverflow.com/questions/20052617/use-multiple-local-strategies-in-passportjs – dimaninc Nov 09 '16 at 09:44
2

And like Hya answered passport dont are good for authorization ...

You need to use middlewares for set admin context and admin roles:

// add this middlewares after your routes...

// set admin context and others things like admin templates
app.use('/admin/*', function adminContext(req, res, next) {
  // set admin context
  req.isAdmin = true;

  next();
});


// then get roles for authenticated user in your passport stategy:
app.use(function getUserRoles(req, res, next) {
  req.userRoleNames = [];

  if (req.isAuthenticated()) {
    req.userRoleNames.push('authenticated');
  } else {
    req.userRoleNames.push('unAuthenticated');
    return next(); // skip role load if dont are authenticated
  }

  // get user roles, you may get roles from DB ...
  // and if are admin add its role
  req.userRoleNames.push('administrator');

  next();

});

// and check roles in admin page
app.get('/admin/admin-page/', function (req, res, next) {
  if (req.userRoleNames.indexOf('administrator') == -1) return res.status(403).send('forbidden');

  console.log('roles:', req.userRoleNames);

  res.status(200).send('Hey you are the administrator!');
});

See we.js core for see one advanced roles and permissions implementation: https://github.com/wejs/we-core

Alberto Souza
  • 681
  • 5
  • 10
  • 1
    Thanks, I've already sighed at @hya's answer and rolled back to the state, where I had roles and middleware to check if a user has a particular role for the route. – Anton Egorov Mar 20 '16 at 09:47