Mongoose User model for handling local and social auth providers
So I found a working solution for myself which might help other people with the same problem. In my User model I have my usual fields and for each social provider I have a separate array like so (users/User.js):
let userSchema = mongoose.Schema({ email: { type: String, unique: true }, name: { type: String }, password: { type: String }, roles: [String], confirmation_code: String, confirmed: { type: Boolean, default: false }, facebook: { id: String, token: String, email: String, name: String }, google: { id: String, token: String, email: String, name: String }}, { timestamps: true });
When authenticating with a social provider I make an extra check if a user with the same email already exists. If it doesn't, I create a new user. If it does I just add the social provider data (id, token, etc.) to the already existing users array like so (config/passport.js):
passport.use(new FacebookStrategy({ clientID: oauth.facebook.clientID, clientSecret: oauth.facebook.clientSecret, callbackURL: oauth.facebook.callbackURL, profileFields: ['id', 'emails', 'name']}, function (accessToken, refreshToken, profile, done) { process.nextTick(function () { User.findOne({ $or: [ { 'facebook.id': profile.id }, { 'email': profile.emails[0].value } ] }, function (err, user) { if (err) { return done(err); } if (user) { if (user.facebook.id == undefined) { user.facebook.id = profile.id; user.facebook.token = accessToken; user.facebook.email = profile.emails[0].value; user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; user.save(); } return done(null, user); } else { let newUser = new User(); newUser.facebook.id = profile.id; newUser.facebook.token = accessToken; newUser.facebook.email = profile.emails[0].value; newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; newUser.name = profile.name.givenName + ' ' + profile.name.familyName; newUser.email = profile.emails[0].value; newUser.save(err => { if (err) { console.log(err); throw err; } return done(null, newUser); }); } }); }); }));
With this approach you can connect one profile with multiple social providers. However there is one downside. If the user registers a new profile for the first time through a social provider, he won't have a password because social providers don't give back password data (duh). He just needs to change (set) his password through his profile afterwards.