Search code examples
node.jsoauthpassport.jsejs

Cannot read properties of undefined (reading 'ES6'),


app.js code -

I don't know why this is getting an error in node module - npm mongoose-findorcreate

I checked the node module but there's no fault in ES6, I'm also attaching the module code below

// jshint esversion:6
require('dotenv').config()
const express=require("express")
const bodyParser = require("body-parser")
const ejs=require("ejs")
const mongoose=require("mongoose")
const bcrypt=require("bcrypt")
const session=require("express-session")
const passport=require("passport")
const flash = require('connect-flash')
const passportLocalMongoose = require("passport-local-mongoose");
const findOrCreate = require("mongoose-findorcreate");
const LocalStrategy=require("passport-local").Strategy;
const GoogleStrategy = require('passport-google-oauth20').Strategy;

const app=express();
app.use(express.urlencoded({extended:false}))  //Not using bodyParser, using Express in-built body parser instead
app.set("view engine","ejs")
app.use(express.static("public"))
app.use(bodyParser.urlencoded({extended: true}));

app.use(session({
    secret:"Justarandomstring.",
    resave:false,
    saveUninitialized:false
}))

app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

mongoose.connect("mongodb://127.0.0.1:27017/userDB")
const userSchema= new mongoose.Schema({
    username : String,
    password : String,
    googleId: String
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);

const User=new mongoose.model("User",userSchema)

//Creating Local Strategy. passport-local-mongoose 3 lines of code for Strategy, 
//Serialiazation, Deserialization not working due to recent changes in Mongoose 7
passport.use(new LocalStrategy((username,password,done)=>{  //done is a callback function
    try{
        User.findOne({username:username}).then(user=>{
            if (!user){
                return done(null,false, {message:"Incorrect Username"})
            }
            //using bcrypt to encrypt passoword in register post route and compare function in login post round. 
            //login post route will check here during authentication so need to use compare here  
            bcrypt.compare(password,user.password,function(err,result){ 
                if (err){
                    return done(err)
                }
                
                if (result) {
                    return done(null,user)
                }
                else {
                    return done (null,false, {message:"Incorrect Password"})
                }
            })
            
        })
    }
    catch (err){
        return done(err)
    }
    
}))
//serialize user
passport.serializeUser(function(user, done) {
    done(null, user.id);
});

//deserialize user  
passport.deserializeUser(function(id, done) {
    console.log("Deserializing User")
    try {
        User.findById(id).then(user=>{
            done(null,user);
        })
    }
    catch (err){
        done(err);
    }
});

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

//get routes
app.get("/",function(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 home.
    res.redirect("/secrets");
  });

app.get("/login",function(req,res){
    console.log(req.flash("error"))
    res.render("login");
})

app.get("/register",function(req,res){
    res.render("register")
})

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

app.get("/logout",function(req,res){
    req.logout(function(err){
        if(err){
            console.log(err)
        }
        res.redirect("/");
    });
    
})

//post routes
app.post("/register",function(req,res){
    bcrypt.hash(req.body.password,10,function(err,hash){  //10 is SaltRounds
        if (err){
            console.log(err)
        }
        const user= new User ({
            username:req.body.username,
            password:hash
        })
        user.save()
        
        passport.authenticate('local')(req,res,()=>{res.redirect("/secrets")}) 
    })
})   

app.post('/login',passport.authenticate('local',
    { successRedirect:"/secrets", failureRedirect: '/login', failureFlash:true}
));

//listen
app.listen(3000, ()=> {
    console.log("Server Running on Port 3000")
})

The module code link:

/*!
 * Mongoose findOrCreate Plugin
 * Copyright(c) 2012 Nicholas Penree <[email protected]>
 * MIT Licensed
 */
function findOrCreatePlugin(schema, options) {
  schema.statics.findOrCreate = function findOrCreate(conditions, doc, options, callback) {
    var self = this;
    // When using Mongoose 5.0.x and upper, we must use self.base.Promise
    var Promise = self.base.Promise.ES6 ? self.base.Promise.ES6 : self.base.Promise;
    if (arguments.length < 4) {
      if (typeof options === 'function') {
        // Scenario: findOrCreate(conditions, doc, callback)
        callback = options;
        options = {};
      } else if (typeof doc === 'function') {
        // Scenario: findOrCreate(conditions, callback);
        callback = doc;
        doc = {};
        options = {};
      } else {
        // Scenario: findOrCreate(conditions[, doc[, options]])
        return new Promise(function(resolve, reject) {
          self.findOrCreate(conditions, doc, options, function (ex, result, created) {
            if (ex) {
              reject(ex);
            } else {
              resolve({
                doc: result,
                created: created,
              });
            }
          });
        });
      }
    }
    this.findOne(conditions, function(err, result) {
      if (err || result) {
        if (options && options.upsert && !err) {
          self.update(conditions, doc, function(err, count) {
            self.findById(result._id, function(err, result) {
              callback(err, result, false);
            });
          });
        } else {
          callback(err, result, false);
        }
      } else {
        for (var key in doc) {
         conditions[key] = doc[key];
        }

        // If the value contain `$` remove the key value pair
        var keys = Object.keys(conditions);

        for (var z = 0; z < keys.length; z++) {
          var value = JSON.stringify(conditions[keys[z]]);
          if (value && value.indexOf('$') !== -1) {
            delete conditions[keys[z]];
          }
        }

        var obj = new self(conditions);
        obj.save(function(err) {
          callback(err, obj, true);
        });
      }
    });
  };
}

/**
 * Expose `findOrCreatePlugin`.
 */

module.exports = findOrCreatePlugin;

I'm getting an error of:

TypeError: Cannot read properties of undefined (reading 'ES6')
    at Function.findOrCreate (C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\node_modules\mongoose-findorcreate\index.js:10:37)
    at Strategy._verify (C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\app.js:101:10)
    at C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\node_modules\passport-oauth2\lib\strategy.js:205:24
    at C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\node_modules\passport-google-oauth20\lib\strategy.js:122:5
    at passBackControl (C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\node_modules\oauth\lib\oauth2.js:134:9)
    at IncomingMessage.<anonymous> (C:\Users\KIIT\Desktop\Web Development\Secrets - Starting Code\node_modules\oauth\lib\oauth2.js:157:7)
    at IncomingMessage.emit (node:events:525:35)
    at endReadableNT (node:internal/streams/readable:1359:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

Solution

  • This is from Angela Yu's Complete Web development Bootcamp. A very small fix for this: Inside the GoogleStrategy just replace the findOrCreate method with findOne.

    passport.use(new GoogleStrategy({
        clientID: process.env.GOOGLE_CLIENT_ID,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        callbackURL: 'http://localhost:3000/auth/google/secrets'
      },
      async function (accessToken, refreshToken, profile, done) {
        try {
          console.log(profile);
          // Find or create user in your database
          let user = await User.findOne({ googleId: profile.id });
          if (!user) {
            // Create new user in database
            const username = Array.isArray(profile.emails) && profile.emails.length > 0 ? profile.emails[0].value.split('@')[0] : '';
            const newUser = new User({
              username: profile.displayName,
              googleId: profile.id
            });
            user = await newUser.save();
          }
          return done(null, user);
        } catch (err) {
          return done(err);
        }
      }
    ));
    

    Here's my complete code for app.js implementing both Google Sign-in and Facebook sign-in:

    require("dotenv").config();
    const express = require("express");
    const app = express();
    const ejs = require("ejs");
    const GoogleStrategy = require('passport-google-oauth20').Strategy;
    const mongoose = require("mongoose");
    const bcrypt = require("bcrypt");
    const session = require("express-session");
    const passport = require("passport");
    const FacebookStrategy = require('passport-facebook').Strategy;
    const LocalStrategy = require("passport-local").Strategy;
    app.use(express.urlencoded({
      extended: false
    })); // Not using bodyParser, using Express in-built body parser instead
    app.set("view engine", "ejs");
    app.use(express.static("public"));
    app.use(session({
      secret: "Our little secret.",
      resave: false,
      saveUninitialized: false
    }));
    app.use(passport.initialize());
    app.use(passport.session());
    mongoose.connect("mongodb://127.0.0.1:27017/userDB");
    const userSchema = new mongoose.Schema({
      username: String,
      password: String,
      googleId: String,
      facebookId: String,
      secret: String
    });
    const User = new mongoose.model("User", userSchema);
    // Creating Local Strategy. passport-local-mongoose 3 lines of code for Strategy,
    // Serialiazation, Deserialization not working due to recent changes in Mongoose 7
    passport.use(new LocalStrategy((username, password, done) => { //done is a callback function
      User.findOne({
        username: username
      }).then(user => {
        if (!user) {
          return done(null, false, {
            message: "Incorrect Username"
          })
        }
        // using bcrypt to encrypt password in register post route and compare function in login post round.
        // login post route will check here during authentication so need to use compare here
        bcrypt.compare(password, user.password).then((isMatch) => {
          if (isMatch) {
            return done(null, user)
          } else {
            return done(null, false, {
              message: "Incorrect Password"
            })
          }
        }).catch((err) => {
          return done(err)
        });
      }).catch((err) => {
        return done(err)
      });
    }));
    // serialize user
    passport.serializeUser(function(user, done) {
      done(null, user.id);
    });
    // deserialize user
    passport.deserializeUser(function(id, done) {
      User.findById(id).then(user => {
        done(null, user);
      }).catch((err) => {
        return done(err)
      });
    });
    passport.use(new GoogleStrategy({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: 'http://localhost:3000/auth/google/secrets'
    }, async function(accessToken, refreshToken, profile, done) {
      try {
        console.log(profile);
        // Find or create user in your database
        let user = await User.findOne({
          googleId: profile.id
        });
        if (!user) {
          // Create new user in database
          const username = Array.isArray(profile.emails) && profile.emails.length > 0 ? profile.emails[0].value.split('@')[0] : '';
          const newUser = new User({
            username: profile.displayName,
            googleId: profile.id
          });
          user = await newUser.save();
        }
        return done(null, user);
      } catch (err) {
        return done(err);
      }
    }));
    passport.use(new FacebookStrategy({
      clientID: process.env.FACEBOOK_APP_ID,
      clientSecret: process.env.FACEBOOK_APP_SECRET,
      callbackURL: 'http://localhost:3000/auth/facebook/secrets'
    }, async function(accessToken, refreshToken, profile, done) {
      try {
        console.log(profile);
        // Find or create user in your database
        let user = await User.findOne({
          facebookId: profile.id
        });
        if (!user) {
          // Create new user in database
          const newUser = new User({
            username: profile.displayName,
            facebookId: profile.id
          });
          user = await newUser.save();
        }
        return done(null, user);
      } catch (err) {
        return done(err);
      }
    }));
    //get routes
    app.get("/", function(req, res) {
      res.render("home");
    });
    app.get("/login", function(req, res) {
      res.render("login");
    });
    app.get("/register", function(req, res) {
      res.render("register");
    });
    app.get("/secrets", function(req, res) {
      User.find({
        secret: {
          $ne: null
        }
      }).then((foundUsers) => {
        res.render("secrets", {
          usersWithSecrets: foundUsers
        })
      }).catch((err) => {
        console.log(err)
      });
    });
    app.get("/logout", function(req, res) {
      req.logout(function(err) {
        if (err) {
          console.log(err);
        }
        res.redirect("/");
      });
    });
    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 home.
      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, redirect to secrets page.
      res.redirect('/secrets');
    });
    app.route("/submit").get(function(req, res) {
      if (req.isAuthenticated()) {
        res.render("submit");
      } else {
        res.rediret("/login");
      }
    }).post(function(req, res) {
      const submittedsecret = req.body.secret;
      User.findById(req.user.id).then((foundUser) => {
        if (foundUser) {
          foundUser.secret = submittedsecret;
          foundUser.save().then(() => {
            res.redirect("/secrets")
          }).catch((err) => {
            console.log(err)
          });
        }
      }).catch((err) => {
        console.log(err)
      });
    });
    // post routes
    app.post("/register", function(req, res) {
      bcrypt.hash(req.body.password, 10, function(err, hash) { // 10 is SaltRounds
        if (err) {
          console.log(err);
        }
        const user = new User({
          username: req.body.username,
          password: hash
        })
        user.save();
        passport.authenticate('local')(req, res, () => {
          res.redirect("/secrets");
        })
      })
    });
    app.post('/login', passport.authenticate('local', {
      successRedirect: "/secrets",
      failureRedirect: '/login'
    }));
    // listen
    app.listen(3000, () => {
      console.log("Server Running on Port 3000");
    });