Skip to content

Passport.js Multiple login system is not working correctly

I am getting a weird error as all of my login system are working correctly which include local login in system and Google and Facebook login. The problem arose when I try to try to register with google when I have already register the Facebook account and I try to register Google account it gives E11000 duplicate key error collection and vice versa for Facebook. I am using passport.js for authentication and login system here is some code:

const express = require("express");
const app = express();
const BodyParser = require('body-parser');
const ejs = require('ejs');
const session = require("express-session");
const passport = require("passport");
const LocalStratgy = require('passport-local').Strategy;
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const FacebookStrategy = require("passport-facebook").Strategy;
const passportLocalMongoose = require("passport-local-mongoose");
const findOrCreate = require('mongoose-findorcreate');

const mongoose = require('mongoose');
const { static } = require("express");
app.use(express.static('public'));
app.set('view engine', 'ejs');
app.use(BodyParser.urlencoded({extended: true}));
app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());


mongoose.connect('mongodb://localhost/userDB', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.set('useCreateIndex', true);
const userSchema = new mongoose.Schema( {
    email: String,
    password: String,
    googleId: String,
    facebookId: String
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);

/* userSchema.plugin(encrypt,{secret: process.env.SECRET, encryptedFields: ['password']}); */


const User = new mongoose.model("User", userSchema);
passport.use(new LocalStratgy(User.authenticate()));
passport.serializeUser(function(user, done) {
    done(null, user.id);
});

passport.deserializeUser(function(id, done) {
    User.findById(id, function(err, user) {
        done(err, user);
    });
});
passport.use(new GoogleStrategy({
    clientID: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRETS,
    callbackURL: "http://localhost:3000/auth/google/secrets",
    userProfileURL: "https://www.googleapis.com/oauth2/v3/userinfo"
},
function(accessToken, refreshToken, profile, cb) {
    
    User.findOrCreate({ googleId: profile.id }, function (err, user) {
        return cb(err, user);
    });
}
));


passport.use(new FacebookStrategy({
    clientID: process.env.FACEBOOK_APP_ID,
    clientSecret: process.env.FB_APP_SECRETS,
    callbackURL: "http://localhost:3000/auth/facebook/secrets"
  },
  function(accessToken, refreshToken, profile, cb) {
      console.log(profile);
    User.findOrCreate({ facebookId: profile.id }, function (err, user) {
      return cb(err, user);
    });
  }
));

app.get("/" ,(req, res) => {
    res.render('home');

});
app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile'] }));
  app.get('/auth/google/secrets', 
  passport.authenticate('google', { failureRedirect: '/login' }),
  function(req, res) {
    // Successful authentication, redirect secrets.
    res.redirect('/secrets');
  });

  app.get('/auth/facebook',
  passport.authenticate('facebook'));
  app.get('/auth/facebook/secrets',
  passport.authenticate('facebook', { failureRedirect: '/login' }),
  function(req, res) {
    // Successful authentication, secrets .
    res.redirect('/secrets');
  });

app.get("/login" ,(req, res) => {
    res.render('login');

});



app.get("/register" ,(req, res) => {
    res.render('register');

});
app.get("/secrets", (req, res) => {
    if(req.isAuthenticated()){
        res.render("secrets");
    }else{
        res.redirect("/login");
    }
})

app.post("/register", (req, res) => {
    User.register({username: req.body.username}, req.body.password, function(err, user){
        if(err){
            console.log(err);
            res.redirect("/register");
        }else{
            passport.authenticate("local")(req, res, function(){
                res.redirect("/secrets");
            })
        }
    })

    
});

app.post("/login", (req, res) => {
    const user = new User({
        username: req.body.username,
        password: req.body.password
    });
    req.login(user, err => {
        if(err){
            console.log(err);
        }else{
            passport.authenticate("local")(req, res, function(){
                res.redirect("/secrets");
            });
        }
    })

});
app.get("/logout", (req, res) => {
    req.logOut();
    res.redirect("/");
})

Note: Individually the Facebook and Google authentication works like a charm but when both used together it throws an error. I store Facebook and Google id in my database as in screenshot: Image of database here and local system works just fine with Facebook and Google no problem there.

Answer

If you follow passport documentation then it’s probably not so correct for your usage.

The following will just create an account base on facebook Id

User.findOrCreate({ facebookId: profile.id }, function (err, user) {
  return cb(err, user);
});

The following will just create an account base on google Id

User.findOrCreate({ googleId: profile.id }, function (err, user) {
    return cb(err, user);
});

However, I’m quite sure your user is identified by email.

Your above queries will create 2 separate _id with same email address.

Hence your query should be corrected to similar as follows

 User.updateOne({ email: profile.email }, { googleId: profile.id }, { upsert: true })

And

 User.updateOne({ email: profile.email }, { facebookId: profile.id }, { upsert: true })

The above will check whether email exist, if it does, update googleId. If it doesn’t exist, create a new user.

In addition, your schema is missing the username field