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.
Solution 1:
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
}
});
Solution 2:
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.
Solution 3:
you can use something like this for multiple local strategies
passport.use('local.one', myLocalStrategy1);
passport.use('local.two', myLocalStrategy2);
passport.use('local.three', myLocalStrategy3);
...
app.get('/login/s1', passport.authenticate('local.one');
app.get('/login/s2', passport.authenticate('local.two');
app.get('/login/s3', passport.authenticate('local.three');
Solution 4:
1)In user registration form save by Default role:'customer' in database 2)In admin registration form save by Default role:'admin' in database
[Note- If you use mongodb as a database in registration schema make-
role:{
type: String,
default: 'customer' or 'admin' //Manage respectively
},
Important- 'This uses cookie for session login'
'You can do this on your own way :)']
For strategy
passport.use('user-local',
new LocalStrategy({ usernameField: 'phone', passwordField: 'password' },
(phone, password, done) => {
// Match user
User.findOne({
phone: phone
}).then(user => {
if (!user) {
return done(null, false, { message: 'The phone not registered' });
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
})
.catch(err => console.log(err));
})
);
passport.use('admin-local',
new LocalStrategy({ usernameField: 'phone', passwordField: 'password' },
(phone, password, done) => {
// Match user
Admin.findOne({
phone: phone
}).then(user => {
if (!user) {
return done(null, false, { message: 'The phone not registered' });
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Password incorrect' });
}
});
})
.catch(err => console.log(err));
})
);
For serialize
passport.serializeUser((user, done) => {
done(null, { _id: user.id, role: user.role });
});
For deserialize
passport.deserializeUser((login, done) => {
if (login.role === 'customer') {
User.findById(login, function (err, user) {
if (user)
done(null, user);
else
done(err, { message: 'User not found' })
});
}
else if (login.role === 'admin') {
Admin.findById(login, (err, admin) => {
if (admin)
done(null, admin);
else
done(err, { message: 'Admin not found' })
});
}
else {
done({ message: 'No entity found' }, null);
}
});