Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use multiple local strategies in PassportJS

I'm trying to use multiple LOCAL strategies with PassportJS. I'm not trying to use local, facebook, and gmail, etc. I have two sets of users stored in separate objects and I want to use a local strategy to authenticate both. As it stands, I cannot use the same local strategy for both because they have different object properties which has me querying different objects. Is there any way to do this? OR any suggestions around this would be greatly appreciated.

like image 976
Plattaz Avatar asked Nov 18 '13 16:11

Plattaz


People also ask

Can Passport use multiple strategies?

Passport's middleware is built in a way that allows you to use multiple strategies in one passport.

What are strategies in Passportjs?

Strategies are responsible for authenticating requests, which they accomplish by implementing an authentication mechanism. Authentication mechanisms define how to encode a credential, such as a password or an assertion from an identity provider (IdP), in a request.


2 Answers

You can name your local strategies to separate them.

// use two LocalStrategies, registered under user and sponsor names        // add other strategies for more authentication flexibility   passport.use('user-local', new LocalStrategy({       usernameField: 'email',       passwordField: 'password' // this is the virtual field on the model     },     function(email, password, done) {       User.findOne({         email: email       }, function(err, user) {         if (err) return done(err);          if (!user) {           return done(null, false, {             message: 'This email is not registered.'           });         }         if (!user.authenticate(password)) {           return done(null, false, {             message: 'This password is not correct.'           });         }         return done(null, user);       });     }   ));      // add other strategies for more authentication flexibility     passport.use('sponsor-local', new LocalStrategy({             usernameField: 'username',             passwordField: 'password' // this is the virtual field on the model         },         function(username, password, done) {             Sponsor.findOne({                 'primaryContact.username': username             }, function(err, sponsor) {                 if (err) return done(err);                  if (!sponsor) {                     return done(null, false, {                         message: 'This email/username is not registered.'                     });                 }                 if (!sponsor.authenticate(password)) {                     return done(null, false, {                         message: 'This password is not correct.'                     });                 }                 return done(null, sponsor);             });         }     )); 

Later controller code refer to them by name.

/**  * User Login  */ exports.loginUser = function (req, res, next) {     passport.authenticate('user-local', function(err, user, info) {         var error = err || info;         if (error) return res.json(401, error);          req.logIn(user, function(err) {              if (err) return res.send(err);             res.json(req.user.userInfo);         });     })(req, res, next); };  /**  * Sponsor Login  */ exports.loginSponsor = function (req, res, next) {     passport.authenticate('sponsor-local', function(err, sponsor, info) {         var error = err || info;         if (error) return res.json(401, error);          req.logIn(sponsor, function(err) {             if (err) return res.send(err);             res.json(req.sponsor.profile);         });     })(req, res, next); }; 

Later when it comes time to serialize you user, you may want to do something like this.

// serialize passport.serializeUser(function(user, done) {               if (isUser(user)) {     // serialize user   } else if (isSponsor(user)) {     // serialize company   } }); 
like image 110
Matthew Payne Avatar answered Sep 27 '22 23:09

Matthew Payne


I don't think it's possible, because as far as I can see you need some method of 'handing off' a request to the second strategy when the first one fails, and I don't believe that's possible.

But you might be able to use one local strategy, and just try to authenticate the incoming data using both methods.

As a simple example (using Mongoose as an example database):

passport.use(new LocalStrategy(function(username, password, done) {   Model1.findOne({ username : username }, function(err, user) {     // first method succeeded?     if (!err && user && passwordMatches(...)) {       return done(null, user);     }     // no, try second method:     Model2.findOne({ name : username }, function(err, user) {       // second method succeeded?       if (! err && user && passwordMatches(...)) {         return done(null, user);       }       // fail!        done(new Error('invalid user or password'));     });   });  })); 

For serialization/deserialization you might need to store some property in the user object that you pass to done to signify which model is required to deserialize the user.

like image 36
robertklep Avatar answered Sep 27 '22 22:09

robertklep