0

I am trying to implement a Node.js endpoint, which updates the password if the timestamp and JWT match. Am I using old syntax here? My code is the following:

app.put('/changepassword', function (req, res) {
  // https://stackoverflow.com/questions/20277020/how-to-reset-change-password-in-node-js-with-passport-js
  // https://www.mongodb.com/blog/post/password-authentication-with-mongoose-part-1
  console.log("Password change endpoint was called by", req.ip);
  User.pre('save', function (next) {
     var user = this;
    // only hash the password if it has been modified (or is new)
    if (!user.isModified('password')) return next();
    
    // generate a salt
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) return next(err);
    
        // hash the password along with our new salt
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) return next(err);
    
            // override the cleartext password with the hashed one
            user.password = hash;
            next();
        });
      });
    });
        
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
  bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
      if (err) return cb(err);
      cb(null, isMatch);
  });
};
});

My error is: TypeError: User.pre is not a function

My UserSchema is the following. It has the keys firstname, lastname, eMail, password, active etc:

const mongoose = require('mongoose');
 
const userSchema = mongoose.Schema({
 
    firstname: {
        type: String,
        required: true
    },
    lastname: String,
    eMail: { 
    type: String, 
    required: true,
    unique: true 
    }, 
    password: String,
    active: Boolean,
    lastlogin: Date,
    loginAttempts: { type: Number, required: true, default: 0 },
    lockUntil: { type: Number }
});
 
/* // expose enum on the model
UserSchema.statics.failedLogin = {
    NOT_FOUND: 0,
    PASSWORD_INCORRECT: 1,
    MAX_ATTEMPTS: 2
  }; */

module.exports = mongoose.model("User", userSchema);
Munchkin
  • 857
  • 5
  • 24
  • 51

2 Answers2

2

Your User is a mongoose model and does not have the pre function. The pre function belongs to middleware.

https://mongoosejs.com/docs/models.html

https://mongoosejs.com/docs/middleware.html#pre

In order to perform the pre operation, you should move your entire pre function call into the file where you declare your userSchema. The pre function is a hook and is something you want to happen every time a save operation is called. There is no need to declare it within the put function.

ChrisG
  • 2,637
  • 18
  • 20
1

As Chris said move the following code to UserSchema File

User.pre('save', function (next) {
     var user = this;
    // only hash the password if it has been modified (or is new)
    if (!user.isModified('password')) return next();
    
    // generate a salt
    bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
        if (err) return next(err);
    
        // hash the password along with our new salt
        bcrypt.hash(user.password, salt, function(err, hash) {
            if (err) return next(err);
    
            // override the cleartext password with the hashed one
            user.password = hash;
            next();
        });
      });
    });
        
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
  bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
      if (err) return cb(err);
      cb(null, isMatch);
  });
};

Once that's done you can access comparePassword function in your method like this,

Dummy Request example

app.get('/comparePasswords', function (req, res) {
  User.findOne(req.id, function (err, user) {
    if (err) throw err;

    
    user.comparePassword(req.password, function (err, isMatch) {
      if (err) throw err;
      console.log(isMatch); // -> Returns True if match
   });
 })

For ChangingPassword I don't think you need to compare passwords, but depends on your usercase

Shivam
  • 3,514
  • 2
  • 13
  • 27