I am trying to use Sequelize's instance method to validate a password on login attempt. I have defined the User model as :

var User = sequelize.define('User',{ id:{ type:DataTypes.BIGINT, autoIncrement: true, allowNull: false, primaryKey:true }, username:{ type:DataTypes.STRING, unique:true }, password:{ type: DataTypes.STRING }, ... }, { classMethods:{ associate:function(models){ ... } } }, { instanceMethods:{ validatePassword:function(password){ return bcrypt.compareSync(password, this.password); } } } ); return User; } 

In my login route I do the following :

  • 1) Retrieve username & password from request body
  • 2) Check if username exists in database
  • 3) If user exists, get user object and compare sent password with hashed password in database using validatePassword method.

Here is the relevant code

var username = req.body.username || ""; var password = req.body.password || ""; models.User.findOne({ where: {username: username} }). then( function(user) { if(user){ console.log(user.validatePassword(password)); } .... 

Each time I try to login I get the following error

[TypeError: user.validatePassword is not a function] 

What am I doing wrong?

2

5 Answers

I think you are using the sequelize model definition api incorrectly.

This is the correct way:

var User = sequelize.define('User',{}, { classMethods: { method1: ... }, instanceMethods: { method2: ... } }); 

not like this:

var User = sequelize.define('User',{}, { classMethods: { method1: ... } },{ instanceMethods: { method2: ... } }); 
1

For anyone who's having a similar problem, I ran into the same issue but using Sequelize 5.21.5. According to this article, Sequelize Instance Methods, starting with Sequelize 4.0 and above, you have to use the prototype methodology in order to define instance methods like so:

 // Adding an instance level methods. User.prototype.validPassword = function(password) { return bcrypt.compareSync(password, this.password); }; 
2

We can add instanceLevelMethods to prototype,

User.prototype.your-instance-level-method-name = function() { return 'foo'; }; 

I did it like this:

// Adding an instance level methods. User.prototype.validPassword = function(password) { return bcrypt.compareSync(password, this.password); }; 
1

as at sequelize "sequelize": "^5.21.7" accessing the instanceMethods as shown by @user1695032 returns undefined.

here's what i found after several hours of getting undefined in the console.log() passing in the user object return from the query below:

User { dataValues: { id: 1, firtName: null, lasteName: null, email: '', phone: null, password: '$2b$10$yEWnBFMAe15RLLgyU3XlrOUyw19c4PCmh8GJe9QVz3YkbdzK5fHWu', createdAt: 2020-05-27T21:45:02.000Z, updatedAt: 2020-05-27T21:45:02.000Z }, _previousDataValues: { id: 1, firtName: null, lasteName: null, email: '', phone: null, password: '$2b$10$yEWnBFMAe15RLLgyU3XlrOUyw19c4PCmh8GJe9QVz3YkbdzK5fHWu', createdAt: 2020-05-27T21:45:02.000Z, updatedAt: 2020-05-27T21:45:02.000Z }, _changed: {}, **_modelOptions: {** timestamps: true, validate: {}, freezeTableName: false, underscored: false, paranoid: false, rejectOnEmpty: false, whereCollection: { email: '' }, schema: null, schemaDelimiter: '', defaultScope: {}, scopes: {}, indexes: [], name: { plural: 'Users', singular: 'User' }, omitNull: false, **instanceMethods: { comparePasswords: [Function: comparePasswords] },** hooks: { beforeValidate: [Array] }, sequelize: Sequelize { options: [Object], config: [Object], dialect: [MysqlDialect], queryInterface: [QueryInterface], models: [Object], modelManager: [ModelManager], connectionManager: [ConnectionManager], importCache: [Object] } }, _options: { isNewRecord: false, _schema: null, _schemaDelimiter: '', raw: true, attributes: [ 'id', 'firtName', 'lasteName', 'email', 'phone', 'password', 'createdAt', 'updatedAt' ] }, isNewRecord: false } 

the code before the error:

 models.User.findOne({where: {email: req.body.email}}).then((user)=>{ console.log(user) if(!user) { res.status(401).json({ message: 'Authentication failed!' }); } else { user.comparePasswords(req.body.password, (error, isMatch) =>{ console.log(error + ' -- ' + isMatch) if(isMatch && !error) { const token = jwt.sign( { username: user.username }, keys.secret, { expiresIn: '30h' } ); res.status(200).json({ success: true,message: 'signed in successfully', token: 'JWT ' + token }); } else { res.status(401).json({ success: false, message: 'Login failed!' }); } }); } }).catch((error)=>{ console.log(error) res.status(500).json({ success: false, message: 'There was an error!'}); }) 

this cause TypeError: user.comparePasswords is not a function

after changing this line:

** user.comparePasswords(req.body.password, (error, isMatch) =>{} ** 

to this:

** user._modelOptions.instanceMethods.comparePasswords(req.body.password, (error, isMatch) =>{}** 

booooom! everything worked

I use this approach:

const bcrypt = require('bcrypt-nodejs'); const constants = require('../constants/users'); module.exports = (Sequelize, type) => { const User = Sequelize.define(constants.TABLE_NAME, { username: { type: type.STRING, unique: true, allowNull: false, }, password: { type: type.STRING, allowNull: false, }, // bla bla }); const setSaltAndPassword = async function(user) { if (user.changed('password')) { const salt = bcrypt.genSaltSync(constants.PASSWORD_SALT_SIZE); user.password = bcrypt.hashSync(user.password, salt); } }; User.prototype.validPassword = async function(password) { return await bcrypt.compare(password, this.password); }; User.beforeCreate(setSaltAndPassword); User.beforeUpdate(setSaltAndPassword); return User; }; 
0

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy