0
votes

Hey I'm new to express and am trying to login as a user. With the following code, I get the error below. I thought since I did module.exports.comparePassword it would work. Is there something I'm missing? Thanks for any help

events.js:160 throw er; // Unhandled 'error' event ^

TypeError: user.comparePassword is not a function at /Users/Sam/Desktop/teach/routes/users.js:112:17 at Query. (/Users/Sam/Desktop/teach/node_modules/mongoose/lib/model.js:3343:16) at /Users/Sam/Desktop/teach/node_modules/kareem/index.js:259:21 at /Users/Sam/Desktop/teach/node_modules/kareem/index.js:127:16 at _combinedTickCallback (internal/process/next_tick.js:67:7) at process._tickCallback (internal/process/next_tick.js:98:9)

models/user.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcryptjs')

var userSchema = new Schema({
  email: { type: String },
  password: { type: String },
  type: { type: String }
});

var User = mongoose.model('User', userSchema);

module.exports = User;

//Get a User by id
module.exports.getUserById = function(id, callback){
  User.findById(id, callback);
}

//Get a User by email
module.exports.getUserByEmail = function(email, callback){
  var query = {email : email}
  User.findOne(query, callback);
}

//Save a student
module.exports.saveStudent = function(newUser, newStudent, callback){
    bcrypt.genSalt(10, function(err, salt) {
        bcrypt.hash(newUser.password, salt, function(err, hash){
            if (err) {throw err}
            newUser.password = hash;
            //Saves both a user and student
            async.parallel([newUser.save, newStudent.save], callback);
        })
    })
}

//Save a instructor
module.exports.saveInstructor = function(newUser, newInstructor, callback){
    bcrypt.genSalt(10, function(err, salt) {
        bcrypt.hash(newUser.password, salt, function(err, hash){
            if (err) {throw err}
            newUser.password = hash;
            //Saves both a user and instructor
            async.parallel([newUser.save, newInstructor.save], callback);
        })
    })
}

//Checks if password matches.
module.exports.comparePassword = function(candidatePassword, hash, callback){
    bcrypt.compare(candidatePassword, hash, function(err, isMatch){
        if(err) throw err;
        callback(null, isMatch);
    });
}

routes/users.js

var express = require('express');
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

var User = require('../models/user');
var Student = require('../models/student');
var Instructor= require('../models/instructor');

router.get('/signup', function(req, res, next) {
  res.render('users/signup');
});

router.get('/login', function(req, res, next){
    res.render('users/login')
});

//Registering a user
router.post('/signup', function(req, res, next){

    var first_name      = req.body.first_name;
    var last_name       = req.body.last_name;
    var email           = req.body.email;
    var password        = req.body.password;
    var password2       = req.body.password2;
    var type            = req.body.type;

    req.checkBody('first_name', 'First name is required.').notEmpty();
    req.checkBody('first_name', 'Please enter a shorter first name.').len(1, 40);
    req.checkBody('last_name', 'Last name is required.').notEmpty();
    req.checkBody('last_name', 'Please enter a shorter last name.').len(1, 40);
    req.checkBody('email', 'Email is required.').notEmpty();
    req.checkBody('email', 'Email must be valid.').isEmail();
    req.checkBody('email', 'Please enter a shorter email.').len(1, 40);
    req.checkBody('password', 'Password is required.').notEmpty();
    req.checkBody('password2', 'Passwords must match.').equals(req.body.password);
    req.checkBody('password', 'Please choose a password between 6 to 50 characters.').len(6, 50);

    var errors = req.validationErrors();

    if(errors){
        res.render('users/signup', {
            errors: errors,
            first_name: first_name,
            last_name: last_name,
            email: email,
            password: password,
            password2: password2
        });
    } else {
        var newUser = new User({
            email: email,
            password: password,
            type: type
        });

        var newStudent = new Student({
            first_name: first_name,
            last_name: last_name,
            email: email,
        });

        var newInstructor = new Instructor({
            first_name: first_name,
            last_name: last_name,
            email: email,
        });

        if(type == 'student'){
            User.saveStudent(newUser, newStudent, function(err, user){
                console.log('Student saved');
            });
        } else {
            User.saveInstructor(newUser, newInstructor, function(err, user){
                console.log('Instructor saved');
            });
        }

        res.redirect('/classes');
    }
});

passport.serializeUser(function(user, done){
    done(null, user._id);
});

passport.deserializeUser(function(id, done){
    User.getUserByEmail(function(err, user){
        done(err, user);
    });
});

//Login in a user
router.post('/login',passport.authenticate('local', {
    failureRedirect:'/users/login', 
    failureFlash:'Wrong Username or Password'
}), function(req, res){
    var usertype = req.user.type;
    res.redirect('/classes');
});

passport.use(new LocalStrategy({
    usernameField: 'email'
  },
    function(email, password, done) {
        User.getUserByEmail(email, function(err, user){
            if (err) return done(err);
            if(!user){
                return done(null, false, { message: 'Unregistered email'}); 
            }
            if (!user.comparePassword(password)) {
                return done(null, false, { message: 'Incorrect password.' });
            }
          return done(null, user);
        });
    }
));

module.exports = router;
2

2 Answers

0
votes

Clearly, it will give an undefined function error since comparePassword is not a function defined for User Schema. You can use it as User.comparePassword since it has the function (in the JS file), but the user (object of the user schema - mongo) has no such function defined.

Do this before you export the User,

userSchema.methods.comparePassword = function(candidatePassword, hash, callback){
    bcrypt.compare(candidatePassword, hash, function(err, isMatch){
        if(err) throw err;
        callback(null, isMatch);
    });
};

Hope it helps.

0
votes

You have 'User' but you are using 'user'. Note the case sensitivity. Use below code:

 passport.use(new LocalStrategy({
        usernameField: 'email'
      },
        function(email, password, done) {
            User.getUserByEmail(email, function(err, user){
                if (err) return done(err);
                if(!user){
                    return done(null, false, { message: 'Unregistered email'}); 
                }
                if (!User.comparePassword(password)) {
                    return done(null, false, { message: 'Incorrect password.' });
                }
              return done(null, user);
            });
        }
    ));

Suggestion: If you are using module.exports than you can export the entire block with it so you need not to write module.exports again and again like you have did.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcryptjs')

var userSchema = new Schema({
  email: { type: String },
  password: { type: String },
  type: { type: String }
});

var User = mongoose.model('User', userSchema);

module.exports = model;

//Get a User by id
model.getUserById = function(id, callback){
  User.findById(id, callback);
}

//Get a User by email
model.getUserByEmail = function(email, callback){
  var query = {email : email}
  User.findOne(query, callback);
}

//Save a student
model.saveStudent = function(newUser, newStudent, callback){
    bcrypt.genSalt(10, function(err, salt) {
        bcrypt.hash(newUser.password, salt, function(err, hash){
            if (err) {throw err}
            newUser.password = hash;
            //Saves both a user and student
            async.parallel([newUser.save, newStudent.save], callback);
        })
    })
}

//Save a instructor
model.saveInstructor = function(newUser, newInstructor, callback){
    bcrypt.genSalt(10, function(err, salt) {
        bcrypt.hash(newUser.password, salt, function(err, hash){
            if (err) {throw err}
            newUser.password = hash;
            //Saves both a user and instructor
            async.parallel([newUser.save, newInstructor.save], callback);
        })
    })
}

//Checks if password matches.
model.comparePassword = function(candidatePassword, hash, callback){
    bcrypt.compare(candidatePassword, hash, function(err, isMatch){
        if(err) throw err;
        callback(null, isMatch);
    });
}

model.schema = User;