how to encrypt passwords with beforeBulkCreate hook in sequelize - node.js

I´m trying to encrypt password before bulk create with sequelize.
const bcrypt = require('bcryptjs');
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('users', {
username: {
type: DataTypes.STRING,
allowNull: false,
required: true
},
password: {
type: DataTypes.STRING,
allowNull: false,
required: true
},
},
{
freezeTableName: true,
hooks: {
beforeBulkCreate: function(records) {
records.forEach((user, index) => {
return bcrypt.hash(user.password, 10)
.then(hash => {
user.password = hash;
console.log('password hash:', user.password);
})
.catch(err => {
throw new Error();
});
})
},
beforeCreate: (user) => {
return bcrypt.hash(user.password, 10)
.then(hash => {
user.password = hash;
})
.catch(err => {
throw new Error();
});
}
}
});
User.prototype.validPassword = (password) => {
return bcrypt.compareSync(password, this.password);
};
return User;
}
hooks is called but the password that´s store in the database is the plain one not the new one
const userData = [
{ username: 'John', password: '123' },
{ username: 'Mary', password: '321' },
];
User.bulkCreate(userData, { returning: true })
.then((result) => {
console.log('User data success');
})
.catch((error) => {
console.log(error);
});
I also tried passing the { individualHooks: true } option but doing this records are not being inserted at all.

I came here in search of an answer to this ~ 3 years old question. Chances are that >= 3 years later someone else will come looking for the same.
Here, I solved mine using bcrypt.hashSync()
I know they recommend using the asynchronous bcrypt.hash() solution but sometimes most optimal isn't always the solution.
You can learn more here bcrypt npm docs
hooks: {
beforeBulkCreate: (users) => {
users.forEach((user) => {
// to see the properties added by sequelize
console.table(user);
// now modify the "dataValues" property
user.dataValues.password = bcrypt.hashSync(user.password, 10);
});
},
// other hooks here
},

Related

How to test login with Bcrypt in Express with Mocha [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
I have implemented a login with hashed password authentication via Bcrypt in my Express Backend. Registration and login, as well as authenticated routes, work fine in the app, but not in the test environment. The following registration test succeeds, but the login test fails. When I log the password inside the user model's compare function, it is logged as plain text instead of the hash. I don't understand why though.
Any help is appreciated.
This is the users.test.js:
import { expect, server, BASE_URL } from './setup'
describe('User', () => {
it('registers a user', (done) => {
const data = { email: 'chester#benning.ton', name: 'chester', password: 'intheend' }
server
.post(`${BASE_URL}/users`)
.send(data)
.expect(201)
.end((err, res) => {
expect(res.status).to.equal(201)
expect(res.body).to.have.property('user')
expect(res.body.user).to.have.property('id')
expect(res.body.user).to.have.property('name', data.name)
done()
})
})
it('logs in a user', (done) => {
const data = { name: 'chester', password: 'intheend' }
server
.post(`${BASE_URL}/users/login`)
.send(data)
.expect(201)
.end((err, res) => {
expect(res.status).to.equal(201)
expect(res.body.user).to.have.property('id')
done()
})
})
})
The user model looks like this:
const { Model, Op } = require('sequelize')
const bcrypt = require('bcrypt')
const config = require('../../config/config')
function hashPassword(user) {
const saltRounds = config.saltRounds
if (!user.changed('password')) {
return false
}
return bcrypt.hash(user.password, saltRounds).then((hashedPassword) => {
user.setDataValue('password', hashedPassword)
})
}
module.exports = (sequelize, DataTypes) => {
class User extends Model {}
User.init({
email: {
allowNull: false,
type: DataTypes.STRING,
unique: true,
validate: {
isUnique(value, next) {
User.findOne({
where: {
email: {
[Op.iLike]: value.trim()
}
}
}).then((user) => {
if (user) {
return next('E-Mail is already registered')
}
return next()
}).catch(() => next('Could not be validated'))
}
}
},
name: {
allowNull: false,
type: DataTypes.STRING,
unique: true,
validate: {
isUnique(value, next) {
User.findOne({
where: {
name: {
[Op.iLike]: value.trim()
}
}
}).then((user) => {
if (user) {
return next('User name already exists')
}
return next()
}).catch(() => next('Could not be validated'))
}
}
},
password: {
allowNull: false,
type: DataTypes.STRING
}
}, {
hooks: {
beforeCreate: hashPassword,
beforeSave: hashPassword,
beforeUpdate: hashPassword
},
modelName: 'User',
sequelize,
tableName: 'Users'
})
User.prototype.comparePassword = async function comparePassword(password) {
return bcrypt.compare(password, this.password).then((res) => {
return res
}).catch((err) => {
return false
})
}
User.prototype.toJSON = function toJSON() {
const userObj = { ...this.get() }
delete userObj.password
return userObj
}
return User
}
And this is my UserController.js:
const { Op } = require('sequelize')
const jwt = require('jsonwebtoken')
const { User } = require('../models')
const config = require('../../config/config')
function jwtSignUser(user) {
const ONE_DAY = 60 * 60 * 24
return jwt.sign(user, config.authentication.jwtSecret, {
expiresIn: ONE_DAY
})
}
module.exports = {
findAll(req, res) {
const options = {
attributes: {
exclude: [ 'password' ]
},
order: [
[ 'name', 'ASC' ]
]
}
return User.findAll(options).then((users) => res.status(200).send(users)).catch(() => {
res.status(404).send({
message: 'Could not find users'
})
})
},
login(req, res) {
const { name, password } = req.body
User.findOne({
where: {
name: {
[Op.iLike]: name
}
}
}).then(async (user) => {
if (!user) {
return res.status(422).send({
message: 'Login information incorrect'
})
}
const passwordIsValid = await user.comparePassword(password)
if (!passwordIsValid) {
return res.status(422).send({
message: 'Login information incorrect'
})
}
const userJson = user.toJSON()
return res.status(201).send({
user: userJson,
token: jwtSignUser(userJson)
})
}).catch(() => {
res.status(500).send({
message: 'Login was not successful'
})
})
},
register(req, res) {
return User.create(req.body).then((user) => {
const userJson = user.toJSON()
return res.status(201).send({
user: userJson,
token: jwtSignUser(userJson)
})
}).catch((error) => {
res.status(422).send({
message: error.errors[0].message
})
})
}
}

How to use bcrypt.compare in sails js schema?

I have a user model like this:
module.exports = {
attributes: {
email: {
type: 'string',
isEmail: true,
unique: true,
required: true
},
password: {
type: 'string',
required: true
}
},
beforeCreate: (value, next) => {
bcrypt.hash(value.password, 10, (err, hash) => {
if (err){
throw new Error(err);
}
value.password = hash;
next();
});
},
};
Now when I want to match the password during login, how do I decrypt the password, if possible I would prefer to perform it in the user model file.
controller/ login.js
module.exports = {
login: async (req, res) => {
try{
const user = await User.findOne({email: req.body.email});
if (!user){
throw new Error('Failed to find User');
}
// here I want to match the password by calling some compare
//function from userModel.js
res.status(201).json({user: user});
}catch(e){
res.status(401).json({message: e.message});
}
},
};
first try to find the user with the given username by user
const find = Users.find(user=>user.username===req.body.username)
if(!find){
res.send('User Not Found')
}
else{
if( await bcrypt.compare(req.body.password,find.password)){
//now your user has been found
}
else{
//Password is Wrong
}
}
You must use bcrypt.compare(a,b)
a = given password by user
b = original password if username exist
hope it solve your problem

How does Sequelize destroy function work?

I've been trying to delete a user in a MySql database using Sequelize ORM but it's not deleting.
without paranoid, here's the query generate:
UPDATE `Users` SET `active`=?,`updatedAt`=? WHERE `id` = ?
with paranoid set to true, here's the query generate:
UPDATE `Users` SET `active`=?,`updatedAt`=? WHERE (`deletedAt` IS NULL AND `id` = ?)
digging around the internet i found out that the the query is supposed to take this form UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1 accounding to this Documentation.
Here's my model:
'use strict';
const sequelizePaginate = require('sequelize-paginate')
const bcrypt = require('bcrypt')
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
photo: DataTypes.STRING,
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: DataTypes.STRING,
phone: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
verifyEmailAt: DataTypes.DATE,
referralCode: DataTypes.STRING,
active: DataTypes.BOOLEAN,
referrer: DataTypes.STRING,
rememberToken: DataTypes.STRING
}, { instanceMethods: {
comparePasswords: (password, prevPassword,callback) => {
bcrypt.compare(password, prevPassword, (error, isMatch) => {
if(error) {
return callback(error);
}
return callback(null, isMatch);
});
}
},
hooks: {
beforeValidate: (user) => {
if(user.changed('password')) {
return bcrypt.hash(user.password, 10).then(function(password) {
user.password = password;
});
}
}
}});
User.associate = (models) => {
// associations can be defined here
models.User.hasMany(models.Booking)
models.User.hasMany(models.DriversLocation)
models.User.hasMany(models.DriverReview)
models.User.hasMany(models.Payment)
};
sequelizePaginate.paginate(User)
return User;
};
And also my query snippet:
const { validationResult } = require('express-validator')
const models = require('../models')
deleteAdmin: (req, res) => {
const result = validationResult(req);
if (!result.isEmpty()) {
return res.status(422).json({ success: false, message: 'input validation failure',
code: 422, properties:{ userId : req.params.userId}, error: result.array() });
}
models.User.destroy({
where: {
id: req.params.userId
}
})
.then((data) => {
return res.status(200).json({ success: true, message: "success",
code: 200, properties:{params: {userId: req.params.userId }},
data: {content: data}});
}).catch((error) => {
return res.status(520).json({ success: false, message: "unknown error",
code: 520, properties:{params: {userId: req.params.userId }},
data: { error: error} });
})
}
Is there something am doing wrong here?
Thank you for your help in advance
destroy usage seems fine to me. Please verify your User model import & req params values.
Also, there is no id field defined in schema definition. How is id being saved in DB record ?

How to set automatic password hashing in Sequelize's user model?

Right now I'm hashing the password on the route function and providing the hash when creating the user but I know there's a way to have this be handled through Sequelize itself. I have searched around but every answer seems to be outdated or the methods never got called. Here is my config:
server/models/User.js
module.exports = (sequelize, type) => {
const User = sequelize.define(
"User",
{
user_id: {
type: type.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: type.STRING,
allowNull: false
},
email: {
type: type.STRING,
allowNull: false
},
password: {
type: type.STRING,
allowNull: false
},
reg_date: {
type: type.DATEONLY,
allowNull: false,
defaultValue: sequelize.fn("now")
}
},
{
timestamps: false
}
);
return User;
};
server/config/sequelize.js
const Sequelize = require("sequelize");
const sequelize = new Sequelize(process.env.CLEARDB_DATABASE_URL);
sequelize
.authenticate()
.then(() => {
console.log("Connection has been established successfully.");
})
.catch(err => {
console.error("Unable to connect to the database:", err);
});
const UserModel = require("../models/User");
const User = UserModel(sequelize, Sequelize);
module.exports = User;
This is how I'm handling hashing right now:
server/routes/register.js
User.findOne({ where: { email: email } }).then(user => {
if (!user) {
bcrypt.hash(password, 10, (err, hash) => {
if (err) throw err;
User.create({
name: req.body.name,
email: email,
password: hash
})
.then(user => {
return user;
})
.catch(err => console.log(err));
});
}
});
This did the trick:
...
{
timestamps: false
}
);
User.addHook(
"beforeCreate",
user => (user.password = bcrypt.hashSync(user.password, 10))
);
return User;
};
Please don't mix your model definition with a business or a security logic (or another one). In future you might want to change an encryption library or a hashing algorithm and you will have to change your model accordingly. A security layer should be separated from your models.

my nodejs bcrypt compare not working properly

am building an app with nodes qraphQl using apollo, am trying to do a login page, but ater signing up and and i try to sign in, my bcrypt would always return false,
in my user model
import bcrypt from 'bcryptjs';
const user = (sequelize, DataTypes) => {
const User = sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
unique: true,
primaryKey: true,
field: 'id'
},
fullname: DataTypes.STRING,
username: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notEmpty: true,
},
},
email: {
type: DataTypes.STRING,
allowedNull: false,
validate: {
notEmpty: true,
isEmail: true,
}
},
password: {
type: DataTypes.STRING,
allowedNull: false,
validate: {
notEmpty: true,
len: [7, 42],
},
},
role: {
type: DataTypes.ENUM,
values: ['ADMIN', 'INSTRUCTOR', 'STUDENT'],
defaultValue: 'STUDENT'
}
});
User.beforeCreate(async user => {
user.password = await user.generatePasswordHash()
});
User.beforeSave(async user => {
user.password = await user.generatePasswordHash()
});
User.prototype.generatePasswordHash = async function() {
const saltRounds = 10;
return await bcrypt.hash(this.password, saltRounds)
};
User.prototype.validatePassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
User.associate = models => {
User.hasMany(models.Message, { onDelete: 'CASCADE' });
};
User.findByLogin = async login => {
let user = await User.findOne({
where: { username: login },
});
if (!user) {
user = await User.findOne({
where: { email: login },
});
}
return user;
};
return User;
};
export default user;
And in my users resolver, here is the code
import { combineResolvers } from 'graphql-resolvers';
import Joi from 'joi'
import { isAuthenticated, isAdmin } from './authorization';
import {SignUp, SignIn} from '../functions/joi'
import {createToken} from '../functions/jwt'
export default {
Mutation: {
signUp: async (parent, { username, fullname, email, password, Rpassword}, { models, secret }) => {
if(password !== Rpassword){
return new Error('Password did not match')
}
var thejoi = { username, fullname, email, password }
const checkUserEm = await models.User.find({ where: { email: email }})
if (checkUserEm) {
return new Error('Email address already Exist')
}
const checkUserUs = await models.User.find({ where: { username: username }})
if (checkUserUs) {
return new Error('Username already Exist')
}
await Joi.validate(thejoi, SignUp, {abortEarly:false})
const user = await models.User.create({
username,
fullname,
email,
password,
role:'STUDENT'
});
return { token: createToken(user) };
},
signIn: async (parent, { login, password }, { models, secret }, ) => {
var varrh = { password }
await Joi.validate(varrh, SignIn, {abortEarly:false})
const user = await models.User.findByLogin(login);
if (!user) {
return new Error('No user found with this login credentials.');
}
const isValid = await user.validatePassword(password);
if (!isValid) {
return new Error('Invalid password .');
}
return { token: createToken(user) };
}
},
User: {
messages: async (user, args, { models }) => {
return await models.Message.findAll({
where: {
userId: user.id
}
});
},
},
}
pls am really confused because its it suppose to work, i have searched google but it didnt help me, pls how can i solve this issue thanks
inside generatePasswordHash you referencing to this, but you also using arrow functions for beforeCreate and beforeSave hooks.
Two options:
Do not use arrow function for beforeCreate and beforeSave hooks.
User.beforeCreate(async function (user) {
user.password = await user.generatePasswordHash()
});
User.beforeSave(async function (user) {
user.password = await user.generatePasswordHash()
});
Provide user object to generatePasswordHash and replace this with user.
User.beforeCreate(async user => {
user.password = await user.generatePasswordHash(user)
});
User.beforeSave(async user => {
user.password = await user.generatePasswordHash(user)
});
User.prototype.generatePasswordHash = async function(user) {
const saltRounds = 10;
return await bcrypt.hash(user.password, saltRounds)
};

Resources