I'm trying to implement login with social media (FB and Google) alongside a local strategy. I have passport-local working fine (other than duplicate emails are allowed but separate question) but I am seeing some strange behavior with the Facebook and Google strategies. Here is a scenario:
I followed the tutorial on this page for setting up the various strategies
http://mherman.org/blog/2013/11/10/social-authentication-with-passport-dot-js/#.Wk93wN-nFPb
Example of a strategy:
passport.use(new googleStrategy({
clientID: config.google.clientID,
clientSecret: config.google.clientSecret,
callbackURL: config.google.callbackURL,
scope: ['profile', 'email'],
passReqToCallback: true
},
(request, accessToken, refreshToken, profile, done) => {
var emailArray = new Array();
profile.emails.forEach((email) => {
emailArray.push(email.value);
})
User.findOne({ $or: [{ oauthID: profile.id }, { email: { $in: [emailArray] } }] }, (err, user) => {
if (err) {
console.log(err);
}
if (!err && user !== null) {
console.log('Already existing user:' + user);
done(null, user);
} else {
console.log('Creating a new user');
user = new User({
email: emailArray[0],
username: profile.name.givenName,
oauthID: profile.id
});
user.save(function (err) {
if (err) {
console.log(err);
} else {
done(null, user);
}
});
}
});
}
));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
The User MongoDB model
var mongoose = require('mongoose'),
passportLocalMongoose = require('passport-local-mongoose');
var userSchema = new mongoose.Schema({
email: String,
username: String,
password: String,
oauthID: Number
});
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', userSchema);
And the auth routes
router.get('/auth/google',
passport.authenticate('google'), (req, res) => {
console.log('Authenticating as: ' + req.user.email + ', oAuthID: ' + req.user.oauthID);
});
router.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/auth' }), (req, res) => {
console.log('Authenticated as: ' + req.user.email + ', oAuthID: ' + req.user.oauthID);
res.redirect('/');
});
The Facebook strategy is basically the same.
My first thought was that the User.findOne()
query was incorrect but if I enter the query into the Mongo console I receive the expected result.
Next I put logging in to see what the value of req.user
was at various points of the authentication process. Every step looks normal except that they ultimately get logged into the account that they originally logged in with.
So I'm not sure where the issue lies...I'm pretty new to web development so any help is / explanations are very appreciated.
A solution I found...
When I was initially setting up the passport-local module, I created the passport.serializeUser()
and passport.deserializeUser()
methods like so:
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
which used the static passport-local-mongoose serialize and deserialize methods.
Changing these too to the functions below solves the issue using the FB or Google strategies and doesn't affect the local strategy
passport.serializeUser((user, done) => {
return done(null, user._id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
if (!err) {
return done(null, user);
} else {
return done(err, null);
}
});
});