Using BCrypt with Sequelize Model - node.js

I'm trying to use the bcrypt-nodejs package with my sequelize model and was tring to follow a tutorial to incorporate the hashing into my model, but I'm getting an error at generateHash. I can't seem to figure out the issue. Is there a better way to incorporate bcrypt?
Error:
/Users/user/Desktop/Projects/node/app/app/models/user.js:26
User.methods.generateHash = function(password) {
^
TypeError: Cannot set property 'generateHash' of undefined
at module.exports (/Users/user/Desktop/Projects/node/app/app/models/user.js:26:27)
at Sequelize.import (/Users/user/Desktop/Projects/node/app/node_modules/sequelize/lib/sequelize.js:641:30)
model:
var bcrypt = require("bcrypt-nodejs");
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('users', {
annotation_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.DATE,
field: 'first_name'
},
lastName: {
type: DataTypes.DATE,
field: 'last_name'
},
email: DataTypes.STRING,
password: DataTypes.STRING,
}, {
freezeTableName: true
});
User.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
User.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
return User;
}

Methods should be provided in the "options" argument of sequelize.define
const bcrypt = require("bcrypt");
module.exports = function(sequelize, DataTypes) {
const User = sequelize.define('users', {
annotation_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.DATE,
field: 'first_name'
},
lastName: {
type: DataTypes.DATE,
field: 'last_name'
},
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
freezeTableName: true,
instanceMethods: {
generateHash(password) {
return bcrypt.hash(password, bcrypt.genSaltSync(8));
},
validPassword(password) {
return bcrypt.compare(password, this.password);
}
}
});
return User;
}

Other alternative: Use hook and bcrypt async mode
User.beforeCreate((user, options) => {
return bcrypt.hash(user.password, 10)
.then(hash => {
user.password = hash;
})
.catch(err => {
throw new Error();
});
});

There's a tutorial out there on how to get a sequelize/postgreSQL auth system working with hooks and bcrypt.
The guy who wrote the tutorial did not use async hash/salt methods; in the user creation/instance method section he used the following code:
hooks: {
beforeCreate: (user) => {
const salt = bcrypt.genSaltSync();
user.password = bcrypt.hashSync(user.password, salt);
}
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
}
}
Newer versions of Sequelize don't like instance methods being declared this way - and multiple people have explained how to remedy this (including someone who posted on the original tutorial):
The original comment still used the synchronous methods:
User.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
All you need to do to make these functions asyncronous is this:
Async beforeCreate bcrypt genSalt and genHash functions:
beforeCreate: async function(user) {
const salt = await bcrypt.genSalt(10); //whatever number you want
user.password = await bcrypt.hash(user.password, salt);
}
User.prototype.validPassword = async function(password) {
return await bcrypt.compare(password, this.password);
}
On the node.js app in the login route where you check the password, there's a findOne section:
User.findOne({ where: { username: username } }).then(function (user) {
if (!user) {
res.redirect('/login');
} else if (!user.validPassword(password)) {
res.redirect('/login');
} else {
req.session.user = user.dataValues;
res.redirect('/dashboard');
}
});
All you have to do here is add the words async and await as well:
User.findOne({ where: { username: username } }).then(async function (user) {
if (!user) {
res.redirect('/login');
} else if (!await user.validPassword(password)) {
res.redirect('/login');
} else {
req.session.user = user.dataValues;
res.redirect('/dashboard');
}
});

Bcrypt Is no longer part of node, so I included example with new module of crypto
I am sharing this code from one of working project.
My config file
require('dotenv').config();
const { Sequelize,DataTypes ,Model} = require("sequelize");
module.exports.Model = Model;
module.exports.DataTypes = DataTypes;
module.exports.sequelize = new Sequelize(process.env.DB_NAME,process.env.DB_USER_NAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOST,
dialect: process.env.DB_DISELECT,
pool: {
max: 1,
min: 0,
idle: 10000
},
//logging: true
});
My user model
const { sequelize, DataTypes, Model } = require('../config/db.config');
var crypto = require('crypto');
class USERS extends Model {
validPassword(password) {
var hash = crypto.pbkdf2Sync(password,
this.SALT, 1000, 64, `sha512`).toString(`hex`);
console.log(hash == this.PASSWORD)
return this.PASSWORD === hash;
}
}
USERS.init(
{
ID: {
autoIncrement: true,
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true
},
MOBILE_NO: {
type: DataTypes.BIGINT,
allowNull: false,
unique: true
},
PASSWORD: {
type: DataTypes.STRING(200),
allowNull: false
},
SALT: {
type: DataTypes.STRING(200),
allowNull: false
}
},
{
sequelize,
tableName: 'USERS',
timestamps: true,
hooks: {
beforeCreate: (user) => {
console.log(user);
user.SALT = crypto.randomBytes(16).toString('hex');
user.PASSWORD = crypto.pbkdf2Sync(user.PASSWORD, user.SALT,
1000, 64, `sha512`).toString(`hex`);
},
}
});
module.exports.USERS = USERS;
And Auth Controller
const { USERS } = require('../../../models/USERS');
module.exports = class authController {
static register(req, res) {
USERS.create({
MOBILE_NO: req.body.mobile,
PASSWORD: req.body.password,
SALT:""
}).then(function (data) {
res.json(data.toJSON());
}).catch((err) => {
res.json({
error: err.errors[0].message
})
})
}
static login(req, res) {
var message = [];
var success = false;
var status = 404;
USERS.findOne({
where:{
MOBILE_NO: req.body.mobile
}
}).then(function (user) {
if (user) {
message.push("user found");
if(user.validPassword(req.body.password)) {
status=200;
success = true
message.push("You are authorised");
}else{
message.push("Check Credentials");
}
}else{
message.push("Check Credentials");
}
res.json({status,success,message});
});
}
}

Old question, but maybe can help someone, you can use sequelize-bcrypt
Example:
const { Sequelize, DataTypes } = require('sequelize');
const useBcrypt = require('sequelize-bcrypt');
const database = new Sequelize({
...sequelizeConnectionOptions,
});
const User = database.define('User', {
email: { type: DataTypes.STRING },
password: { type: DataTypes.STRING },
});
useBcrypt(User);
Usage
User.create({ email: 'john.doe#example.com', password: 'SuperSecret!' });
// { id: 1, email: 'john.doe#example.com', password: '$2a$12$VtyL7j5xx6t/GmmAqy53ZuKJ1nwPox5kHLXDaottN9tIQBsEB3EsW' }
const user = await User.findOne({ where: { email: 'john.doe#example.com' } });
user.authenticate('WrongPassword!'); // false
user.authenticate('SuperSecret!'); // true

Related

Routing in NodeJs

I am working on an eCommerce application, and I am trying to save users in my database but when I hit the API in the postmen then instead of :
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
**instead of this code following code is running
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
//the complete code of my "auth.js" file is as following:
const User = require("../models/user");
exports.signup = (req, res) => {
const user = new User(req.body);
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
};
exports.signout = (req, res) => {
res.json({
message: "User signout"
});
};
///and the complete code of my user model file is as following:
var mongoose = require("mongoose");
const crypto = require("crypto");
const uuidv1 = require("uuid/v1");
var userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
maxlength: 32,
trim: true
},
lastname: {
type: String,
maxlength: 32,
trim: true
},
email: {
type: String,
trim: true,
required: true,
unique: true
},
userinfo: {
type: String,
trim: true
},
encry_password: {
type: String,
required: true
},
salt: String,
role: {
type: Number,
default: 0
},
purchases: {
type: Array,
default: []
}
},
{ timestamps: true }
);
userSchema
.virtual("password")
.set(function(password) {
this._password = password;
this.salt = uuidv1();
this.encry_password = this.securePassword(password);
})
.get(function() {
return this._password;
});
userSchema.methods = {
autheticate: function(plainpassword) {
return this.securePassword(plainpassword) === this.encry_password;
},
securePassword: function(plainpassword) {
if (!plainpassword) return "";
try {
return crypto
.createHmac("sha256", this.salt)
.update(plainpassword)
.digest("hex");
} catch (err) {
return "";
}
}
};
module.exports = mongoose.model("User", userSchema);
SO please anyone tell me how to solve this problem while hiting this code to api mongo shell is also opend and mean while i also keep ROBO3T connected.

Nodejs bcrypt compare not working properly

I am building an app with nodes qraphQl using apollo and I am trying to do a login page, but after 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 function(user) {
user.password = await user.generatePasswordHash(user)
});
User.beforeSave(async function(user) {
user.password = await user.generatePasswordHash(user)
});
User.prototype.generatePasswordHash = async function(user) {
const saltRounds = 10;
return await bcrypt.hash(user.password, saltRounds)
};
User.prototype.validatePassword = async function(password) {
console.log(this.password)
const theReturn = await bcrypt.compare(password, this.password)
console.log(theReturn)
return theReturn;
};
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
}
});
},
},
}
when i tried to signup, it worked, it stored the hassed password in the database, but when i tried to signIn i got this error message
{
"errors": [
{
"message": "Invalid password .",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"signIn"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Invalid password .",
" at signIn (C:\\Users\\De Stone Of David\\Desktop\\node projects\\vue\\cybersec\\server\\src\\resolvers\\user.js:65:16)"
]
}
}
}
],
"data": null
}
In the console i got this
Executing (default): INSERT INTO `users` (`id`,`fullname`,`username`,`email`,`password`,`role`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'nsalknlsa','stones4semper','Eloike95#gmail.com','$2a$10$eX8zvI7/EJv6N.2RzbBh9e.qKoJXtmDNDw22nAY6dixTi4btWCB6G','STUDENT','2019-02-17 09:51:44','2019-02-17 09:51:44');
Executing (default): SELECT `id`, `fullname`, `username`, `email`, `password`, `role`, `createdAt`, `updatedAt` FROM `users` AS `user` WHERE `user`.`username` = 'Eloike95#gmail.com' LIMIT 1;
Executing (default): SELECT `id`, `fullname`, `username`, `email`, `password`, `role`, `createdAt`, `updatedAt` FROM `users` AS `user` WHERE `user`.`email` = 'Eloike95#gmail.com' LIMIT 1;
$2a$10$eX8zvI7/EJv6N.2RzbBh9e.qKoJXtmDNDw22nAY6dixTi4btWCB6G
false
Please I am really confused because its suppose to work, i have searched google but it didn't help me, how can i solve this issue? Thanks in advance.
OK so I faced the same problem and the solution is this.
In your user model file
line :- const theReturn = await bcrypt.compare(password, this.password)
here password has already hashed the thing with compare or compareSync is that the first parameter should be the unhashed password that you enter in the login form.
The second parameter is an already hashed password that you want to compare your data with.
So all you have to do is not hash the password, because you are already hashing it and then sending it into the compare function it gets hashed twice. So you get an invalid password.
FYI, compare is used and required to handle the Promise; and compareSync is used, without a Promise. Also, compareSync returns a boolean value.
Hope that helps, thanks!

Instance Methods is not working in Sequelize

I can't seem to set the User model properly. I've implemented some instance methods that don't seem to be working
var UserDetails = mysequelize.sequelize.define('user_tb', {
id: {
autoIncrement: true,
type: mysequelize.sequelize.Sequelize.INTEGER,
allowNull: false,
},
username: {
type: mysequelize.sequelize.Sequelize.STRING,
primaryKey: true,
unique: true,
},
hierarchyid: {
type: mysequelize.sequelize.Sequelize.STRING
},
password: {
type: mysequelize.sequelize.Sequelize.STRING,
validate: {
len: {
args: [6, 15],
msg: "Please enter a password with at least 6 chars but no more than 15"
}
},
allowNull: false
},
customerid: {
type: mysequelize.sequelize.Sequelize.INTEGER
},
statususer: {
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
userid: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
authtoken: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
}
},
{
tableName: 'user_tb',
timestamps: false,
freezeTableName: true
});
UserDetails.prototype.toJSON = function(){
var user = this;
var userObject = user.toObject();
return _.pick(userObject,['userid','password']);
};
UserDetails.prototype.findByCredentials = function(userid, password)
{
console.log('Sunddep');
var User = this;
// return User.fin
User.findOne({userid}).then((user)=> {
if(!user)
{
return Promise.reject();
}
return new Promise((resolve,reject) => {
bcrypt.compare(password,user.password,(err,res) => {
if(res)
{
resolve(user);
}
else{
reject();
}
})
})
});
}
UserDetails.prototype.generateAuthToken = function()
{
var user = this;
var access = 'authtoken';
var token = jwt.sign({userid:
user.userid.toHexString(),access},process.env.JWT_SECRET).toString();
user.build({
access: token
});
user.save().then(() =>{
return token;
});
}
module.exports = {UserDetails}
server.js
app.post('/user/login', (req, res) => {
console.log(req.body);
var body = _.pick(req.body, ['userId', 'password']);
user.findByCredentials(body.userId, body.password).then(() => {
res.send('Sundeep');
},
(e) => {
sendData: ({
wsState: '0',
messageCode: 'WS000001',
message: 'No user find with this Id',
userData: []
});
res.status(400).send(sendData)
});
});
Hi, I am getting error while calling instance method from other class. Can any one tell me how can i achieve it
UserDetails.prototype.findByCredentials = function(userid, password) {.....}
But while run the server.js file i getting error like UserDetails.findByCredentials is not a function while calling from other class
Thanks for help in advance.

Sequelize - beforeCreate not being called

I am trying to create a user model with sequelize in nodejs, but it doesn't seem that my beforeCreate isn't actually being called. I have looked at the documentation and multiple examples on the internet but I can't get it working.
The model code is as follows:
"use strict";
const bcrypt = require('bcrypt');
require('dotenv').config()
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false
},
emailAddress: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
lastLogin: {
type: DataTypes.DATE,
allowNull: true
}
});
function cryptPassword(password, callback) {
bcrypt.genSalt(process.env.SALT_ROUNDS, function(err, salt) {
if (err)
return callback(err);
bcrypt.hash(password, salt, function(err, hash) {
return callback(err, hash);
});
});
};
User.beforeCreate(function(model, options) {
cryptPassword(model.password, function(err, hash) {
model.password = hash;
});
});
return User;
};
you should define your hook inside of your model
var User = sequelize.define("User", {
username: ...
}, {
hooks: {
beforeCreate: function(){...}
}
});

Cannot access Sequelize instance methods

I get the following error when I attempt to call the generateHash instance method I've defined on my User model:
User.generateHash(...).then is not a function
Here's the model definition itself:
const User = sequelize.define('User',
{
firstName: {
type: Sequelize.TEXT,
field: 'first_name'
},
lastName: {
type: Sequelize.TEXT,
allowNull: false,
field: 'last_name'
},
userName: {
type: Sequelize.TEXT,
field: 'user_name',
allowNull: false
},
password: {
type: Sequelize.TEXT,
allowNull: false
}
}, {
tableName: 'users',
underscored: true,
classMethods: {
associate: function(models) {
User.hasMany(
models.Trip,
{
as: 'trips',
foreignKey: {
name: 'userId',
field: 'user_id',
allowNull: false
},
onDelete: 'CASCADE'
});
},
},
instanceMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validatePassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
apiRepr: function() {
return {
id: this.id,
firstName: this.firstName,
lastName: this.lastName,
userName: this.userName
};
}
}
});
Here's the endpoint where I attempt to call the method:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
// if no existing user, hash password
return User.generateHash(password)
.then(hash => {
// create new user
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
});
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
res.status(500).json({message: 'Internal server error'});
});});
I'm totally stuck. Any ideas?
In sequelize V4 class and instance methods are removed.
Now you have to make it this way:
const Model = sequelize.define('Model', {
...
});
// Class Method
Model.associate = function (models) {
...associate the models
};
// Instance Method
Model.prototype.someMethod = function () {..}
More information here Sequelize v4 breaking changes
You are calling .then() on something that does not return a promise. Try this:
router.post('/', (req, res) => {
let {userName, password, firstName, lastName} = req.body;
let hash = User.generateHash(password);
// if no existing user, hash password
return User.create({
firstName: firstName,
lastName: lastName,
userName: userName,
password: hash
})
.then(user => {
// send back apirRepr data
return res.status(201).json(user.apiRepr());
})
// error handling
.catch(err => {
if (err.name === 'AuthenticationError') {
return res.status(422).json({message: err.message});
}
return res.status(500).json({message: 'Internal server error'});
});

Resources