NodeJS Passport: multilogin via two strategies - serialize issue

Problem

I'd like to provide user ability to login with one of two strategies: via Instagram and usual Local strategy. For local I've written simple function to find user in DB and provide it's user info. This is how it works:

passport.use(new InstagramStrategy({
    clientID: global.INSTAGRAM_CLIENT_ID,
    clientSecret: global.INSTAGRAM_CLIENT_SECRET,
    callbackURL: "http://**.**.**.**:3000/auth/instagram/callback"
  },
  function(accessToken, refreshToken, profile, done) {
    process.nextTick(function () {
        console.log('Access token: ' + accessToken);
        global.access_token = accessToken;
      return done(null, profile);
    });
  }
));

passport.use(new LocalStrategy(
    function(email, password, done) {
    console.log('chec user');
    process.nextTick(function() {
        db.findByEmail(email, function(err, user) {
            if (!user) {
                console.log('Unknown user ' + email);
                return done(null, false, {
                    message: 'Unknown user ' + email
                });
            }
            if (user.password != crypt.getMD5fromString(password)) {
                console.log('Invalid password');
                return done(null, false, {
                    message: 'Invalid password'
                });
            }
            return done(null, user);
        })
    });
}));

However my custom serialize and deSerialize functions don't work with Instagram strategy:

    /* THESE FUNCTIONS WORK WITH INSTAGRAM STRATEGY
passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(obj, done) {
  done(null, obj);
});*/

    /*THESE ARE FOR LOCAL STRATEGY*/
passport.serializeUser(function(user, done) {done(null, user._id);});
passport.deserializeUser(function(id, done) {db.findById(id, function(err, user) {done(err, user);});});

How to avoid this bug? My aim – to provide login with Instagram, and after successull login check - if user has local account (by e-mail or Instagram nickname), get data from DB. Although user should have ability to login without Instagram via local strategy. Thanks.

Problem courtesy of: f1nn

Solution

The short answer is: Use the same User object type for BOTH the local and third party verify callbacks. You probably want to set this up in your Instagram verify callback: instead of calling done(null,profile) you may want to create a new User object, assign myUser.instagram = profile, and callback done(null, myUser) or something like that.

I have some example code that demonstrates one way to set up multiple authentication systems. You can find it here: https://github.com/therealplato/passport-multiauth-demo/blob/master/app.js

Couple caveats: This was written for Express 2.x which handles the server differently from 3 and has a different syntax for sending responses. edit, demo code updated for express 3.x

Additionally the Google method here is Oauth 1. I cannot get Oauth 1 google auth to work on localhost, it gives an error ...has no method 'CharChodeAt'...

Funnily enough I spent today working on getting Oauth 2 to play nicely with my local users. So if I can find time I will try to update the demo code for Express 3 and Oauth 2 but no promises :)

Solution courtesy of: Plato

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.