Reset password in node js - node.js

Im trying to add reset password feature.User enters username,email and full name,when these values match data from database ,password is changed,otherwise an error is shown.
This is what im doing-
app.post("/reset",function(req,res){
User.findByUsername(req.body.username).then(function(sanitizedUser){
if (sanitizedUser){
sanitizedUser.setPassword(req.body.password, function(){
sanitizedUser.save();
req.flash("success","password resetted");
res.redirect("/login");
});
} else {
req.flash("error","User doesnt exist");
res.redirect("/reset");
}
},function(err){
console.log(err);res.redirect("/");
});
});
But i want to compare more than just the username,i want to compare email and name of the user too.And when i add-
User.find({username:req.body.username},{email:req.body.email},{name:req.body.name})and enter some wrong data,the page just keeps reloading rather than showing an error.
What changes should i make to do that?Please help.
Im using express,nodejs,mongodb

You have a redirect loop.
In the case of !sanitizedUser you redirect to the same page res.redirect('/reset').
This is causing your page to keep reloading.
You should change the line:
res.redirect('/reset')
to
res.status(500).send('Some useful error message')

if(req.body.username && req.body.email && req.body.name && req.body.password){
//add logic to hash password below
let hashedPassword = req.body.password;
Model.findOneAndUpdate(
{ username : req.body.username, email : req.body.email, name : req.body.name },
{ "$set" : { password : hashedPassword } },
//if below option "new" is set to true, call back will return new document else it will return old document beforeupdate
{ new : true },
//call back
function(err, person){
if(err){
//err found
res.send(err);
}
else{
// no error and also person exists
res.send('Password successfully updated..');
}
}
);
}
else{
res.send('Please provide all details..');
}

Related

Mongoose-encryption Failure, Page keeps rolling

I am having a problem with an npm package called 'mongoose-encryption'. Here is my code.
main().catch((err) => console.log(err));
async function main() {
await mongoose.connect('mongodb://localhost:27017/userEncryptedDB', { useNewUrlParser: true });
}
const userSchema = new mongoose.Schema({
email: String,
password: String
});
const secret = 'xxs4Ox4nSKSVnJzIxzy+es6ouOmoMcqcarAnEVRP26Q=';
userSchema.plugin(encrypt, {secret: secret, encryptedFields: ['password']});
const User = mongoose.model('User', userSchema);
Now, if I don't use the package, registration and logging in to the website works properly but if I use the plug in, it says "You are never registered!" even though I register for the email and password...
app.post('/login', (req, res) => {
const email = req.body.username;
const password = req.body.password;
User.findOne({email: email, password: password}, (err, user) => {
if(err) {
console.error(err);
} else {
if(user){
console.log('You are already registered!');
res.render('secrets');
} else {
console.error('You are never registered!');
}
}
});
});
What am I doing wrong here? I started computer stopped database, restarted it. Registration is successfully done but when I try to login page keeps rolling... If I dont use mongoose-encryption, login page works fine once I enter password.
I can clearly see that the new username gets added to the database like so:
{ "_id" : ObjectId("616a4aa45dfbf877d9f92468"), "email" : "aras#aras.com", "_ct" : BinData(0,"YbVYW9Pi6dz1yqRD7XgcEyEp48lLGVjp3J40ZgxGt/YuA8+eyiyebMaO9AGdygBj6A=="), "_ac" : BinData(0,"YQiV2QvS9AyrE1hjm81x5U8K6+0lT8h1ruxtCEZv/iwZWyJfaWQiLCJfY3QiXQ=="), "__v" : 0 }
But then when I try to login, it fails to retrieve password. Password I use here in this case is "1".
Try dropping the collection (first save it somewhere for later use) and try again. It will work!

Bcrypt-NodeJS compare() returns false whatever the password

I know that question has already been asked a few times (like here, here or there, or even on Github, but none of the answers actually worked for me...
I am trying to develop authentication for a NodeJS app using Mongoose and Passport, and using Bcrypt-NodeJS to hash the users' passwords.
Everything was working without any problem before I decided to refactor the User schema and to use the async methods of bcrypt. The hashing still works while creating a new user but I am now unable to verify a password against its hash stored in MongoDB.
What do I know?
bcrypt.compare() always returns false whatever the password is correct or not, and whatever the password (I tried several strings).
The password is only hashed once (so the hash is not re-hashed) on user's creation.
The password and the hash given to the compare method are the right ones, in the right order.
The password and the hash are of type "String".
The hash isn't truncated when stored in the database (60 characters long string).
The hash fetched in the database is the same as the one stored on user's creation.
Code
User schema
Some fields have been stripped to keep it clear, but I kept the relevant parts.
var userSchema = mongoose.Schema({
// Local authentication
password: {
hash: {
type: String,
select: false
},
modified: {
type: Date,
default: Date.now
}
},
// User data
profile: {
email: {
type: String,
required: true,
unique: true
}
},
// Dates
lastSignedIn: {
type: Date,
default: Date.now
}
});
Password hashing
userSchema.statics.hashPassword = function(password, callback) {
bcrypt.hash(password, bcrypt.genSaltSync(12), null, function(err, hash) {
if (err) return callback(err);
callback(null, hash);
});
}
Password comparison
userSchema.methods.comparePassword = function(password, callback) {
// Here, `password` is the string entered in the login form
// and `this.password.hash` is the hash stored in the database
// No problem so far
bcrypt.compare(password, this.password.hash, function(err, match) {
// Here, `err == null` and `match == false` whatever the password
if (err) return callback(err);
callback(null, match);
});
}
User authentication
userSchema.statics.authenticate = function(email, password, callback) {
this.findOne({ 'profile.email': email })
.select('+password.hash')
.exec(function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, false);
user.comparePassword(password, function(err, match) {
// Here, `err == null` and `match == false`
if (err) return callback(err);
if (!match) return callback(null, false);
// Update the user
user.lastSignedIn = Date.now();
user.save(function(err) {
if (err) return callback(err);
user.password.hash = undefined;
callback(null, user);
});
});
});
}
It may be a "simple" mistake I made but I wasn't able to find anything wrong in a few hours... May you have any idea to make that method work, I would be glad to read it.
Thank you guys.
Edit:
When running this bit of code, match is actually equal to true. So I know my methods are correct. I suspect this has something to do with the storage of the hash in the database, but I really have no idea of what can cause this error to occur.
var pwd = 'TestingPwd01!';
mongoose.model('User').hashPassword(pwd, function(err, hash) {
console.log('Password: ' + pwd);
console.log('Hash: ' + hash);
user.password.hash = hash;
user.comparePassword(pwd, function(err, match) {
console.log('Match: ' + match);
});
});
Edit 2 (and solution) :
I put it there in case it could be helpful to someone one day...
I found the error in my code, which was occurring during the user's registration (and actually the only piece of code I didn't post here). I was hashing the user.password object instead of user.password.plaintext...
It's only by changing my dependencies from "brcypt-nodejs" to "bcryptjs" that I was able to find the error because bcryptjs throws an error when asked to hash an object, while brcypt-nodejs just hashes the object as if it were a string.
I know the solution has been found but just in case you are landing here out of google search and have the same issue, especially if you are using a schema.pre("save") function, sometimes there is a tendency of saving the same model several times, hence re-hashing the password each time. This is especially true if you are using references in mongoDB to create schema relationship. Here is what my registration function looked like:
Signup Code
User.create(newUser, (err, user) => {
if (err || !user) {
console.warn("Error at stage 1");
return res.json(transformedApiRes(err, "Signup error", false)).status(400);
}
let personData: PersonInterface = <PersonInterface>{};
personData.firstName = req.body.first_name;
personData.lastName = req.body.last_name;
personData.user = user._id;
Person.create(personData, function (err1: Error, person: any): any {
if (err1 || !person) {
return res.json(transformedApiRes(err1, "Error while saving to Persons", false));
}
/* One-to-One relationship */
user.person = person;
user.save(function (err, user) {
if (err || !user) {
return res.json({error: err}, "Error while linking user and person models", false);
}
emitter.emit("userRegistered", user);
return res.json(transformedApiRes(user, `Signup Successful`, true));
});
});
});
As you can see there is a nested save on User because I had to link the User model with Person model (one-to-one). As a result, I had the mismatch error because I was using a pre-save function and every time I triggered User.create or User.save, the function would be called and it would re-hash the existing password. A console statement inside pre-save gave me the following, showing that indeed that password was re-hashed:
Console debug after a single signup call
{ plain: 'passwd',
hash: '$2b$10$S2g9jIcmjGxE0aT1ASd6lujHqT87kijqXTss1XtUHJCIkAlk0Vi0S' }
{ plain: '$2b$10$S2g9jIcmjGxE0aT1ASd6lujHqT87kijqXTss1XtUHJCIkAlk0Vi0S',
hash: '$2b$10$KRkVY3M8a8KX9FcZRX.l8.oTSupI/Fg0xij9lezgOxN8Lld7RCHXm' }
The Fix, The Solution
To fix this, you have to modify your pre("save") code to ensure the password is only hashed if it is the first time it is being saved to the db or if it has been modified. To do this, surround your pre-save code in these blocks:
if (user.isModified("password") || user.isNew) {
//Perform password hashing here
} else {
return next();
}
Here is how the whole of my pre-save function looks like
UsersSchema.pre("save", function (next: NextFunction): any {
let user: any = this;
if (user.isModified("password") || user.isNew) {
bcrypt.genSalt(10, function (err: Error, salt: string): any {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function (err: Error, hash: string) {
if (err) {
console.log(err);
return next(err);
}
console.warn({plain: user.password, hash: hash});
user.password = hash;
next();
});
});
} else {
return next();
}
});
Hopefully this helps someone.
I am dropping this here because it might help someone someday.
In my own case, the reason why I was having bcrypt.compare as false even when I supplied the right authentication details was because of the constraints on the datatype in the model. So each time the hash was saved in the DB, it was truncated in order to fit into the 50 characters constraints.
I had
'password': {
type: DataTypes.STRING(50),
allowNull: false,
comment: "null"
},
The string could only contain 50 characters but the result of bcrypt.hash was more than that.
FIX
I modified the model thus DataTypes.STRING(255)
bcrypt.hash() has 3 arguments... you have 4 for some reason.
Instead of
bcrypt.hash(password, bcrypt.genSaltSync(12), null, function(err, hash) {
it should be
bcrypt.hash(password, bcrypt.genSaltSync(12), function(err, hash) {
Since you were hashing only during user creation, then you might not have been hashing properly. You may need to re-create the users.
Tip: If you are switching
then().then()
Block always check return value.
You can always check the max length for the password field in the database. Make sure it is large. In my case, I have set it to 500. And then the code worked flawlessly!
TS version
const { phone, password } = loginDto;
const user = await this.usersService.findUserByPhone(phone);
const match = await compare(password, user.password);
if (user && match){
return user
}else{
throw new UnauthorizedException();
}
JS version
const { phone, password } = loginDto;
const user = await this.usersService.findUserByPhone(phone);
const match = await bcrypt.compare(password, user.password);
if (user && match){
return user
}else{
throw new UnauthorizedException();
}

Using connect-flash with sequelize

I am having trouble getting connect flash to work in the sequelize callback function.
router.route('/')
.post(function(aRequest, aResponse) {
var data = aRequest.body;
models.users.findOne({
where: {
email: data.email
}
}).then(function (aUser) {
if (!aUser) {
bcrypt.hash(data.password, null, null, function(err, hash) {
if(err) {
return next(err);
}
models.users.create({
firstname : data.firstname,
lastname : data.lastname,
email : data.email,
password : hash
}).then(function () {
aRequest.flash('success', 'User successfully created.');
}).done(function () {
aResponse.redirect('/login');
});
});
}
else {
// aRequest.flash('error', 'This email address is already registered');
aResponse.redirect('/login');
}
});
});
Above is my current code, have tried a few variations on it, calling both flash and redirect in the .then(), tried 2 .then()'s and now the .done().
I am getting the following error:
Unhandled rejection TypeError: undefined is not a function
at Instance.set (/home/node/shared/Heroku/landbou/node_modules/sequelize/lib/instance.js:348:68)
Which is easily resolved by removing aRequest.flash(...).
And yes, 'router.use(flash());' is being called higher up.
Everything continues so its not an app breaking error, but I do need the messages to flash up, otherwise I have to create additional wasted routes to handle the success/fail for user registrations.

Why is my Node validation failing in this POST?

I'm following along in the MEAN machine Node authentication tutorial.
Here is their source code: https://github.com/scotch-io/mean-machine-code/blob/master/10-node-authentication/server.js I basically have everything except for the apiRouter.post('/authenticate', part
The Express APIs are working:
http://localhost:8615/api/users will return a list of users from scotch.io's MongoDB
The following is the API for /api/users:
apiRouter.route('/users')
// create a user (accessed at POST http://localhost:8615/api/users)
.post(function(req, res) {
// create a new instance of the User model
var user = new User();
// set the users information (comes from the request)
user.name = req.body.name;
user.username = req.body.username;
user.password = req.body.password;
// save the user and check for errors
user.save(function(err) {
if (err) {
// duplicate entry
if (err.code == 11000)
return res.json({ success: false, message: 'A user with that username already exists. '});
else
return res.send(err);
}
// return a message
res.json({ message: 'User created!' });
});
})
// get all users (access at GET http://localhost:8615/api/users)
.get(function(req, res) {
User.find(function(err, users) {
if (err) return res.send(err);
// return the users
res.json(users);
})
});
Here is my user.js User Schema
// SCHEMAS ------------------------------------
// user schema
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false }
// ^ select false will not return passwords
});
// hash the password before the user is saved
UserSchema.pre('save', function(next) {
var user = this;
// PUT username
if (!user.isModified('username')) return next();
// PUT name
if (!user.isModified('name')) return next();
// hash the password only if the password has been changed or user is new
if (!user.isModifited('password')) return next();
// generate the salt
bcrypt.hash(user.password, null, null, function(err, hash) {
if (err) return next(err);
// change the password to the hashed version
user.password = hash;
next();
});
});
FROM THE BOOK:
Create a Sample User
First, we need to make sure that we even have a user to authenticate
since towards the end of last chapter, we deleted everyone. Let’s
create the user using the POST http://localhost:8080/api/users route
we created in our API to add a user to our database.
We will send a POST request with the following information: Name Chris
Username chris Password supersecret
I'm using Postman to add a new user, as you can see I have put in key/value pairs for username and password, however getting an error saying "Validation failed" "username is required" "password is required":
UPDATE, I just tried x-www-form-urlencoded and got the following error
GET /api/users 200 66.141 ms - 655
••• API CRUD hit •••
/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27
if (!user.isModifited('password')) return next();
^
TypeError: Object { password: 'supersecret',
username: 'Chris',
name: 'chris',
_id: 54c001dc4ee4028c18e61334 } has no method 'isModifited'
at model.UserSchema.methods.comparePassword.user (/Users/leongaban/NodeDallas/projects/awesome-test/app/models/user.js:27:12)
Screenshot of error in Postman: https://s3.amazonaws.com/f.cl.ly/items/1M0M392M0E3b2i430I1n/Image%202015-01-21%20at%201.45.51%20PM.png
try x-www-form-urlencoded in postman, that would do.
You want to push a json to your server. Select raw and set the data type to JSON.
Then you just have to write your user in JSON format with all his fields here.
{
"name": "Chris",
"username": "chris",
"password": "supersecret"
}

$or search using Mongoose

I am trying to find whether the user is entering a duplicate name and/or email address. I am new to Mongoose (and MongoDb for that matter), but based on what I have read, this appears to be correct. However, it returns true - no matter what. If I do a find for either one individually, not as an $or, it seems to work fine.
// check if user exists
var userExists = function(u, callback) {
User.find({$or:[ {'username': u.username}, {'email': u.email}]} , function(err,user) {
if (err) { // err, so not sure if user exists
callback(1);
return;
}
if (user) { // user, so return exists
callback(1);
return;
}
//no error, no user
callback(0);
});
};
thoughts?
updated method to check for user.length instead of whether it was null. If the document is not found by Mongoose (or MongoDB), it does not return null.
// check if user exists
var userExists = function(u, callback) {
User.find({$or:[ {'username': u.username}, {'email': u.email}]} , function(err,user) {
if (err || user.length > 0) { // user does not come back null, so check length
callback(1);
return;
}
//no error, no user
callback(0);
});
};

Resources