Reputation: 5235
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 :
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?
Upvotes: 22
Views: 18799
Reputation: 2064
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;
};
Upvotes: 2
Reputation: 75
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: '[email protected]',
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: '[email protected]',
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: '[email protected]' },
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
Upvotes: 1
Reputation: 131
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);
};
Upvotes: 13
Reputation: 1789
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);
};
Upvotes: 10
Reputation: 1142
I think you are using the sequelize model definition api incorrectly. http://docs.sequelizejs.com/en/latest/docs/models-definition/#expansion-of-models
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: ...
}
});
Upvotes: 14