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?
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