Search code examples
javascriptbcryptmongoose-schema

Accessing user password variable using this instance inside another function


Am trying to authenticate a user but i get this error in postman

{
"error": "data and hash arguments required"
}

My user model looks like this:

const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const SALT_WORK_FACTOR = 10
const Schema = mongoose.Schema

const UserSchema = new Schema({
  username: { type: String, required: true, index: { unique: true } },
  password: { type: String, required: true }
})
UserSchema.pre('save', function (next) {
  let 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, (err, salt) => {
    if (err) return next()

    // hash the password along with our new salt
    bcrypt.hash(user.password, salt, (err, hash) => {
      if (err) return next(err)
      // override the cleartext password with the hashed one
      user.password = hash
      next()
    })
  })
})
UserSchema.methods.comparePassword = (candidatePassword, callback) => {
  console.log('Password', this.password)// undefined
  bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
    callback(err, isMatch)
  })
}
module.exports = mongoose.model('userData', UserSchema, 'userData')

I figured out that this.password passed to bcrypt compare function resolves to undefined .How can I access the password variable defined inside the UserSchema instance above?


Solution

  • Apparently in javascript, this is not assigned a value until an object invokes the function, thereby binding this to the method's owner (the invoking object). In the above question I am using es6 arrow functions whose this binds to the immediate surrounding scope (lexical scope),thus couldn't find property password which is defined on the UserSchema and not bound to the scope of this in the arrow function.
    Example:

    var Utils = {
      printName: function(somename) {
        console.log(somename)
      }
    }
    
    var person = {
      firstName: "Gregory",
      lastNames: ["Baltimore", "Barry", "Derrick", "Evanson"],
      fullName: function() {
        this.lastNames.forEach(lastname => {
         //this points to person object
          Utils.printName(this.firstName + " " + lastname) //output Gregory Baltimore Gregory Barry Gregory Derrick Gregory Evanson
        })
      }
    }
    
    person.fullName()
    

    Note this inside the es6 arrow function points to the person object which is its static scope(lexical).Lets take a look at the same piece of code from an es5 perspective

     var Utils = {
          printName: function(somename) {
            console.log(somename)
          }
        }
    
        var person = {
          firstName: "Gregory",
          lastNames: ["Baltimore", "Barry", "Derrick", "Evanson"],
          fullName: function() {
            this.lastNames.forEach(function(lastname) {
        //this points to global object
              Utils.printName(this.firstName + " " + lastname)  //output undefined Baltimore undefined Barry undefined Derrick undefined Evanson 
            })
          }
        }
    
        person.fullName()
    

    You will notice that firstname is printed as undefined.This is because in es5 this is out of scope and thus by default(a Javascript quirk) binds to the global object.In the browser this global object is the window in Nodejs the global object is the Nodejs environment(runtime) if not defined by the user.(e.g as in a module export) To solve my question i defaulted to using es5 function,since the password property is bound to the global object which i have defined as UserSchema.Thus the password property was correctly resolved

     UserSchema.methods.comparePassword = function (candidatePassword, callback) {
      //Now this is bound to the UserSchema object
      bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
        callback(err, isMatch)
      })
    }
    

    More of this can be found here Understand JavaScript’s “this” With Clarity, and Master It

    and here too Arrow functions and the ‘this’ keyword