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'});
});
Related
I have recently tried to add the column "last_sign_in_at" to the User table (named "UsersTable" in our db) using sequelize. I've created the migration file and run the migration:
module.exports = {
async up (queryInterface, Sequelize) {
return queryInterface.addColumn('UsersTable', 'last_sign_in_at', {
type: Sequelize.DATE,
});
},
async down (queryInterface, Sequelize) {
return queryInterface.removeColumn('UsersTable', 'last_sign_in_at');
}
};
I have updated the User model in Node.js:
module.exports = (sequelize, DataTypes) => {
let User = sequelize.define(
'User',
{
firstName: DataTypes.STRING,
middleName: DataTypes.STRING,
lastName: DataTypes.STRING,
verificationKey: DataTypes.STRING,
verifiedDate: DataTypes.DATE,
LocationId: DataTypes.INTEGER,
...
gender: DataTypes.INTEGER,
ethnicity: DataTypes.INTEGER,
metro_location_id: DataTypes.INTEGER,
newToSalesType: {
type: DataTypes.INTEGER,
field: 'new_to_sales_type_id',
},
newToSalesStatus: {
type: DataTypes.INTEGER,
field: 'new_to_sales_status_id',
},
countryId: {
type: DataTypes.INTEGER,
field: 'country_id',
},
lastSignInAt: {
type: DataTypes.DATE,
field: 'last_sign_in_at',
}
},
I have a route that is supposed to update this value every time a user logs in and is authenticated:
router.authenticate = (req, res, next) => {
passport.authenticate('local', function (err, user, info) {
if (err) {
return set403Response(res);
}
if (!user) {
models.User.findAll({
where: {
email: {$ilike: req.body.email}
}
}).then(users => {
if (users.length > 0) {
if (!users[0].dataValues.isLinkedinLogin && !users[0].dataValues.isFacebookLogin) {
return set403Response(res, {emailExists: true});
}
}
return set403Response(res);
});
} else {
req.logIn(user, function (err) {
if (err) {
return set403Response(res);
}
models.UserLogin.write(user);
models.User.update(
{ lastSignInAt: Date.now() },
{ where: { user_id: user.user_id } }
).then(()=>{
console.log('TRACE 3')
return res.redirect('/api/currentUser');
})
});
}
})(req, res, next);
}
The field is showing up in PGAdmin:
last_sign_in_at in the db
I'm even able to make a SQL query to update the value of last_sign_in_at in my Query Tool for individual rows.
HOWEVER! When I try to log in a user in my app, my server is telling me:
SequelizeDatabaseError: column User.last_sign_in_at does not exist
I'm completely stumped.
I am newbie and working for my thesis.
I was able to create insert and delete function but getting issues updating the data.
The below is the code under the controllers folder:
Insert Document:
module.exports.register = (params) => {
let user = new User({
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
})
return user.save().then((user, err) => {
return (err) ? false : true
})
}
Delete Document:
module.exports.deleteUser = (params) => {
return User.findByIdAndRemove(params.userId).then((user, err) => {
return (err) ? false : true
})
}
While the below is the code for the routers:
Insert Document:
router.post('/register', (req, res) => {
UserController.register(req.body).then(result => res.send(result))
});
Delete Document:
router.delete('/delete/:id', (req, res) => {
let userId = req.params.id
UserController.deleteUser({userId}).then(user => res.send(user))
});
What I've tried to far is to use the delete logic but instead of findOneAndRemove, I used findOneAndUpdate but it's not updating the data. It just sending true value but not updating the document.
I've also tried some of the logic in YouTube and some here but it doesn't matched the way we construct the data so I'm having difficulties understanding them.
The target is to update the same values in the register using the id of the user.
I'm trying to update using this code:
Controllers:
module.exports.updateUser = (params) => {
return User.findOneAndUpdate(params.userId, (user, err) => {
return (err) ? false : true
})
}
Routes:
router.post('/update/:id', (req, res) => {
UserController.updateUser(req.params.id).then(user => res.send(user))
});
I've also tried to add the parameters but it's not working. What I want to update is the whole details example:
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
Sample User:
{
"userId": "60f649bd8896c80004b3ffbe",
"firstName": "Jane",
"lastName": "Joe",
"department": "Accounting",
"position": "head",
"email": "janedoe#mail.com",
"mobileNo": "0",
"password": "pass123",
"isAdmin": "yes",
"departments": {
"departmentId": "60efcbec769cf60004b85319"
}
}
Here's my update code:
Controller:
module.exports.updateUserData = (params) => {
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hash(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
}
Routers:
router.post('/update-user/:id', (req, res) => {
let userId = req.params.id;
UserController.updateUserData({userId}).then(doc => res.send(doc))
})
Using this code I am getting error message: TypeError: Cannot read property 'then' of undefined under routers folder.
You should try following. It might meet your requirement
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
I have a problem and I cannot find a similar post for my problem, I want to go on a route, I have a message "Could not read property 'findOne' of undefined" but my model is good and works, I have it have tested with findall() in the file and works ....
I use express, sequelize and passeport.js in the back-end
Thank you in advance for your help
const { User } = require('../models')
const LocalStrategy = require('passport-local').Strategy
const passport = require('passport')
const bcrypt = require('bcrypt')
const verifPass = (password, user) => {
return bcrypt.compareSync(password, user.user_password)
}
passport.serializeUser((user, done) => {
done(null, user.user_email)
})
passport.deserializeUser((email, done) => {
User.findOne({where: {user_email: email}}).then(user => {
done(null, user)
}).catch(err => {
done(err, null)
})
})
passport.use(new LocalStrategy(
function(email, password, done) {
User.findOne({where: {user_email: email}}).then(user => {
if (!user){
return done(null, false)
}
if (!verifPass(password, user)){
return done(null, false)
}
return done(null, user)
}).catch(err => {
return done(err, null)
})
}
));
module.exports = passport
The user model
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate({ Role, Role_User }) {
// define association here
this.belongsTo(Role, {foreignKey: "user_role_id" })
}
};
User.init({
user_name: {
type: DataTypes.STRING,
allowNull: false
},
user_surname: {
type: DataTypes.STRING,
allowNull: false
},
user_phone: {
type: DataTypes.STRING,
allowNull: false
},
user_email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
isEmail: true
}
},
user_last_connection: {
type: DataTypes.DATE,
allowNull: false
},
user_password: {
type: DataTypes.STRING,
allowNull: false
},
user_function: {
type: DataTypes.STRING,
allowNull: true
},
user_data_hiring: {
type: DataTypes.DATE,
allowNull: true
},
user_data_departure: {
type: DataTypes.STRING,
allowNull: true
},
user_notes: {
type: DataTypes.STRING,
allowNull: true
},
user_role_id: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: "Roles",
key: "id"
}
},
isDeleted: {
type: DataTypes.BOOLEAN,
allowNull: false,
}
}, {
sequelize,
modelName: 'User',
}, );
return User;
};
error log
-------------------EDIT-----------------
fix the first error message by create method outside passport.use
const findUser = async (email) => {
return await User.findOne({where: {user_email: email}})
}
passport.use(new LocalStrategy.Strategy({
usernameField: 'email',
passwordField: 'password'
}, async (email, password, done) => {
try {
const user = await findUser(email)
console.log(user)
if (user && verifPass(password, user)){
done(null, user)
}else{
done(null, false)
}
}catch (error) {
done(error)
}
}))
but now an other unknow function...
log error 2
I really don't understand in the doc it doesn't make sense to happen like that.
the login controller :
router.post('/',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.json(req.user)
});
-------------------SOLUTION-----------------
After several hours, I found that it was actually a sensible dependency to save sessions that was causing the problem: https://www.npmjs.com/package/connect-session-sequelize
I replaced it with: https://www.npmjs.com/package/express-mysql-session and it works perfectly
I get an error creating a post and trying to associate it with the current user:
SequelizeDatabaseError: Field 'userId' doesn't have a default value
this is my method
exports.createPost = async (req, res) => {
const {title, content, userEmail} = req.body;
const user = await User.findOne({
where: {
email: userEmail
}
})
if (user) {
const post = await Post.create({
title, content, image: req.file.originalname, userId: user.id
})
res.json(post)
}
}
Tried with user: user as well.
This is my user model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
User.associate = function(models) {
User.hasMany(models.Post)
};
return User;
};
This is the post model, where the belongsTo relation is declared
'use strict';
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
content: DataTypes.STRING,
image: DataTypes.STRING
}, {});
Post.associate = function(models) {
Post.belongsTo(models.User, {
onDelete: 'CASCADE'
})
};
return Post;
};
you have not define association in right way . you can do like this .
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
id: {
type: DataTypes.INT,
autoIncrement: true,
primaryKey: true,
},
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
User.associate = function (models) {
User.hasMany(models.Post, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
};
return User;
};
Post Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
id: {
type: DataTypes.INT,
autoIncrement: true,
primaryKey: true,
},
userId: DataTypes.INT,
title: DataTypes.STRING,
content: DataTypes.STRING,
image: DataTypes.STRING
}, {});
Post.associate = function (models) {
Post.belongsTo(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
};
return Post;
};
Query be Like :
const { title, content, userEmail } = req.body;
const user = await User.findOne({
where: {
email: userEmail
},
raw: true
})
if (user) {
const post = await Post.create({
title, content, image: req.file.originalname, userId: user.id
})
res.json(post)
}
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