Mongoose Pre Command not working as Expected - node.js

Hi I am New to Mongoose And trying to use pre command to Convert a given password in String to Hash values before saving it in the database. I am not Getting any Syntax error but my password is not getting converted into Hash IDS.
My Controller Code is as follows:
User.save(req.body,function(err,data){
if (err) {
res.json({
sucess:false,
exception:err
});
}
else{
res.json({
sucess:true,
User:data
});
}
});
User.pre('save',function(next){
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt){
if(err){
return res.json({
success:false,
exception:err
});
}
bcrypt.hash(password, salt, function(err, hash) {
if(err){
return res.json({
success:false,
exception:err
});
}
password=hash;
});
});
next();
});
Using Node inspector I found out that command line is not entering the User.pre .So Can somebody let me know where I am making a mistake.

There's various things wrong with your code, the main part being that hooks should be attached to the schema and not the model, which seems to be what you're doing.
Also, it looks like you're trying to add a pre-save hook for every request, which is not how hooks are supposed to work: they should be declared just once, on the schema (as said above) and before you create the model.
The order should be similar to this:
var UserSchema = new mongoose.Schema(...);
UserSchema.pre('save', ...);
var User = mongoose.Model(UserSchema);
This means that you can't send an error response from your pre-save hook, as you're trying to do now, but you don't really want that anyway IMO (you should keep your Mongoose schema/models separated from your Express route handlers). Instead, catch any errors that are thrown during .save() and handle them there.
To give an idea on what a proper hook might look like, here's an example (I took a guess as to your User schema so it may be using the wrong field names):
UserSchema.pre('save', function(next) {
var document = this;
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(document.password, salt, function(err, hash) {
if (err) return next(err);
document.password = hash;
next();
});
});
});

Related

Node js - Bcrypt - compare method returns false for the correct input password

I've been through various similar questions here and i tried them all, but still, the result is always "wrong password".
I'm using bcrypt for nodejs and my password hashes are stored in an Postgresql database.
The hash and compare methods are as follow :
generateHash : function(password, callBack){
bcrypt.genSalt(8, function(err, salt) {
bcrypt.hash(password, salt, callBack);
});
}
validPassword : function(password, callBack){
bcrypt.compare(password, this.password, callBack);
}
I'm using these function in the following piece of code :
//Generating hashing and DB storing
User.generateHash(password, function(err, hash) {
// if there is no user with that email
// create the user
var newUser = User.build({
email: email,
password: hash
})
.save()
.then(function(newUser) {
return done(null, newUser);
})
.catch(function(err){
return done(err, false);
});
});
//...
//Checking the password input for login
user.validPassword(password, function(err, res) {
if(err) throw err;
if(!res){
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
}
else{
// all is well, return successful user
return done(null, user);
}
});
I hope that was clear. Thanks in advance. Ciao.
Update I : callBack added to validPassword, although this didn't fix the problem. And i have also checked the this.password value, it's correct and as expected. So, the problem is still present.
I think you forgot to add callBack as parameter to
validPassword : function(password){
Try if adding that solves your problem, so change it to
validPassword : function(password, callBack){
Also, I don't know where your validPassword function is in, but you might want to check if this.password does indeed refer to the users password.
i just solved the problem. lol it was a series of errors that made this hard to figure it out. So i'm just going to enumerate what must be done to avoid such things :
The hash must be stored in the database as a varchar and not as a char. The latest cause the hash to be of a non correct length, and so the comparison will fail. varchar is the solution to this.
Handling the comparison result must be done inside of the callBack function. This is due to nodejs being asynchronous. This was correct (see code in the question) i just want to point it out. Otherwise, the result of the comparison would be undefined.
I hope this will help some of you.

MongoDB (mongoose) in Nodejs - save acts strange

I use this in router in nodejs to save a value posted on html.
router.post('/', function(req,res,next) {
User.findOne({ '_id': req.user._id }, function (err, doc){
console.log(doc);
if(err) { console.log(err);
res.json({success: false, msg: 'Error: database error.'}); };
doc.profile.name = req.body.value;
doc.save();
console.log(doc);
return res.json({success: true});
});
});
when I output the doc after I save it works perfectly but after I refresh or look at the database itself it did not modify at all.
It used to work. What I saw and I don't get why, is if I use doc.save() , then again doc.save() two times it works and updates the data eventually.
Can someone explain what may happen?
So for someone who can not debug the problem , you can use .
.save(function (err,obj){
console.log(err);
});
for me it was a problem with the structure of my db because I've added some enums and I had empty field so that caused the problem.

Bcrypt error: illegal arguments String unidentified

here is my complete code
var express = require('express'),
app = express(),
mongoose = require('mongoose'),
bodyParser = require('body-parser'),
morgan = require('morgan'),
webToken = require('jsonwebtoken'),
bcrypt = require('bcryptjs'),
assert = require('assert');
Schema = mongoose.Schema,
secretKey = "omjdiuwkslxmshsoepdukslsj";
//User Schema
var userSchema = new Schema({
username: {type: String, required: true, index: {unique:true}},
password: {type: String, required: true, select: false}
})
userSchema.pre('save', function(next){
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash){
if(err) return next(err);
user.password = hash;
next();
})
});
userSchema.methods.comparePassword = function(password){
var user = this;
bcrypt.compare(password, user.password, function(err, result){
if(err){
console.log(err);
}
else {
console.log("passwords match!");
return;
}
})
}
var userModel = mongoose.model('users', userSchema);
//Connecting to Mongo
mongoose.connect("mongodb://localhost/userstories", function(err){
if(err) {
console.log(err);
}
else {
console.log("Connected to database!");
}
});
//Creating Token
function createToken(user){
var token = webToken.sign({
_id: user.id,
username: user.username
}, secretKey,{
expiresIn: 30 * 60 * 1000
})
return token;
}
//Middlewares
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(morgan('dev'));
//Api's
app.post('/signup', function(req, res){
var user = new userModel({
username: req.body.username,
password: req.body.password
})
user.save(function(err){
if(err){
console.log(err);
}
else{
res.json({message: "User created!"});
}
})
})
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, function(err, user){
if(err) console.log(err);
if(!user){
res.send("User not found!");
}
else if(user){
var validPassword = user.comparePassword(req.body.password);
if(validPassword){
var tokens = createToken(user);
res.json({
success: true,
message: "Successfully logged In",
token: tokens
});
}
else {
res.send("Invalid password");
}
}
})
});
//Running the server
app.listen(3000, function(err){
if(err) console.log("port not working");
else{
console.log("Everything went just fine");
}
})
I've tried every approaches and saw all the answers here. But no one seem to come across this illegal argument error. Please figure this one out for me Im sure there is a bug I cant see
In your User Schema, you are setting select to false for the password field. This means that anytime you look for a user in the schema as you're trying to do in the login request, you won't get the value of the password field or any other field that has select false defined in the schema.
What you need to do is to specify you need the password when the user is found:
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, 'password', function(err, user){
// continue
}
This will return only the _id and the password from the DB. If you want to return other fields, you'd have to add them in:
app.post('/login', function(req, res){
userModel.findOne({username: req.body.username}, 'password firstName lastName email', function(err, user){
// continue
}
I have tried the same code for authentication once and got the same error Error: Illegal arguments: string, function.
But I did not see any issue with the code. The thing is, I had registered two users with the same user name and a different password. Then, when I tried to log in with the user name and one password this error occurred and stopped the server.
So it seems that you are also facing the same problem. Make sure there are no errors with this stuff if you do not want to have a bug in your code.
Check the value of user.password before sending it to bcrypt.compare().
Chances are, you've fetched the user without including the password property, resulting in a value of undefined. This can happen in Sequelize if you set custom attributes or if you're using a scope that excludes props.
Your code is wrong in this place. You may see it.
var validPassword = user.comparePassword(req.body.password);
If you use bcryptjs thrid party plugins, like that
let validPassword = bcrypt.compare(req.body.password, user.password);
bcrypt.compare(password, hashedPassword);
In my particular case, I was dealing with this error, checking out all the code up and down unsuccessfully for almost two days.
Finally, realized that the column PASSWORD in MariaDB was in uppercase. Theoretically that shouldn't affect at all, but I decided to rename it to lowercase and bum! problem solved.
For those using async/await for database calls, make sure you don't forget the await keyword on the User.findOne() call.
In my case, I had forgotten the await keyword while fetching the user. This as a result, was giving me a Promise object instead of User object and hence the password property on it was undefined.
I also encountered the same error when I was using
bcrypt.compareSync("input to be compared with the hash", hash).
Later on I discovered that I was supposed to pass the actual value in the first input parameter i.e (The actual value from which the hash was generated) and the hash in the second input parameter, but I was passing hashed values in both the input parameters.
After correcting the same it was giving me the desired output as true or false.
You can also run and check your code here.
Do like this:
UserSchema.pre('save', async function (next) {
const hash = await bcrypt.hash(this.password, 10);
this.password = hash;
next()
})
You need to specify that you also want the password because you have set the select property to false on password.
So when you are fetching the user, just make sure to explicitly specify that you also want the password. Add .select('+password') on the user object when you are querying a user.
In my case it was a simple spelling mistake before sending to bcrypt.hash :
If you're testing this with Postman, I just found an issue where the default Content-Type header is set to text/plain. If you untick the default header (as it doesn't allow you to change it) and add another Content-Type header with a value of application/json, it works.

Checking whether a document present in mongodb using mongoose

I am trying to create a nodejs application using mongodb as database. I need to check whether a username exist in mongodb or not. If username present, it will output "username not available", else will list all the user details with that username.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.find({ username: "bob" }).exec(function(err,display) {
if(err){
console.log(err);
}
else
{
if(display=='[]'){
res.send("username not available");
}
else{
res.send(display)
}
}
});
};
Is there any alternative or simple way for performing this operation?
I would decouple the whole logic from the controller if you ask me, but I'm not going to lecture you on that. I would use findOne as that will find just one record. If the user is not available, you will know that this username is not available. Don't forget that even when an error occurs, you still want to output "some" data, as you don't want the client to wait until it times out.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.findOne({username: "bob"}).exec(function(err, user) {
if (err) {
console.log(err);
// Handle the error properly here, we should not continue!
return res.sendStatus(500);
}
if (!user) {
return res.send("username not available");
}
// Don't know what you want to do with it, I just display it like this
return res.json(user);
});
};

Mongoose Returned Model can't be updated

I'm pretty new to Mongoose/Mongo and node.js, so I suspect this is just a misunderstanding on my side, but...
The code sample below is the smallest failing example, not specifically my use case.
var User = app.db.model('User');
User.find({email: 'm8#test.com'}, function (err, models) {
models[0].update(function(err, mod) {
console.log(err.message)
});
});
This results in the following error: After applying the update to the document {_id: ObjectId('54647402cb955748153ea782') , ...}, the (immutable) field '_id' was found to have been altered to _id: ObjectId('546d9e0e539ed9ec102348f9')
Why is this happening? I would have thought calling update on the model returned from the initial find would have been fine.
Please note: in my use case there are things happening in between the find and the update. Specifically, I'm doing something similar to:
model.property.push(objectId)
Which I then want to commit via the update.
I'm sure this is a straight-forward issue, but I can't see anywhere in the docs I may be getting it wrong.
All help appreciated.
UPDATE:
What I actually needed to do was:
var User = app.db.model('User');
User.find({email: 'm8#test.com'}, function (err, models) {
models[0].save(function(err, mod) {
console.log(err.message)
});
});
Using 'save' rather than 'update'
I don't know if I understood
Find and Update ( for example using express )
var email = req.params.email;
User.find({email:email}, req.body, function(err,user){
if(err){
throw err;
}
//you do stuff like this
var obj = {
password:'new pass',
username:'username'
}
//use save if you want validate
User.update(user[0],obj,function(err, mod) {
console.log(err)
});
});
Only Update: ( for example using express )
User.update({email:email}, req.body, {}, function(err,user){
if(err){
throw err;
}
res.send(200, {
message : 'User updated ' + user
});
});
Remember that:
A model is a compiled version of the schema.
I hope this can help you

Resources