Search code examples
node.jsmongodbpassport-localpassport-facebookpassport-google-oauth2

Login with passport for social logs user into the same account


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:

  1. Log in with Facebook or Google
  2. Sign out
  3. Log in with the opposite of whichever the initial log in was (i.e. first FB, second Google)
  4. The app logs in with the initial account rather than the second.

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.


Solution

  • 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);
            }
        });
    
    });