Im trying to build a node api for change password,
User must type the currentPassword and the new password
when bcrypt.compare the new currentPassword with the stored on db, i got always false, whatever it's wrong or correct
const changePass = async (req, res, next) => {
//email and password
const CurrentPassword = req.body.currPassword
let password1 = ''+req.body.password1
let password2 = ''+req.body.password2
const hashedPassword = await bcrypt.hash(password1, 10);
let id = "" + req.body.id
User.findById( id )
.then(user => {
bcrypt.compare(CurrentPassword, user.password, (err, data) => {
if (err) throw err
if (data) {
User.findByIdAndUpdate(id, {password : hashedPassword }, {new: false}, (err) => {
if (err) throw err
})
} else {
return res.status(401).json({ msg: "Invalid" })
}
})
})
}
If you want to learn bcrypt I recommend you to visit bcrypt NPM because it will save you too much time later,
in your case I made some modification on your code in order to check for the current password OLD and then compare between the newPassword1 and the confirmation passwordConfirmation
feel free to use console.log('') when you have doubts about anything it will give you a good vision about your code status
const changePassword = async (req, res, next) => {
let id = req.body.nid;
if(id){
console.log('Im here')
const old = req.body.old;
const newP = req.body.newP;
const newP2 = req.body.newP2;
User.findById(id,(err,user)=>{
if(user){
console.log(user)
const hash = user.password;
bcrypt.compare(old,hash,function (err,res){
if(res){
if(newP === newP2){
bcrypt.hash(newP,10, (err,hash)=>{
user.password = hash;
user.save( (err,user) =>{
if(err) return console.error(err);
console.log(user.userName +' your password has been changed');
});
});
};
};
});
}
})
}
}
Related
For my registration page, I want to check for existing users before adding a new user to avoid duplicate usernames. For some reason, after I check if an existing username exists in the database, it does not run any of the code afterwards.
app.post("/register", (req, res) => {
const username = req.body.username;
const password = req.body.password;
var newuser = false;
db.query("SELECT * FROM users WHERE username = ?;", [username], (err, result) => {
if (err) {
res.send({err: err})
}
if (result.length == 0) {
newuser = true;
console.log(newuser + "one")
}
})
if (newuser) {
console.log(newuser + "two")
bcrypt.hash(password, saltRounds, (err, hash) => {
if (err)
console.log(err)
db.query(
"INSERT INTO users (username, password) VALUES (?,?)",
[username, hash],
(err, result) => {
if (err)
console.log(err);
}
)
})
}
})
In this example, the only output I get is "trueone" ... meaning that it evaluated result.length to 0 and set newuser = true.
It is because you are not waiting to the result of the first select. You can use async function or move the if(new user) condition into the select callback.
In my project, I've different roles (seller/user/admin)and i want to check the role and redirect to specific page if they are seller for example.
I struggle on how i can check the role in Mongo DB before the login. My login page is basic email-password and submit button.
for my signup all is good, it's use the correct model and post it in the DB.
here are some pieces of my code:
(client model)
userSchema.statics.login = async function (email, password, role) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error("incorrect password");
}
throw Error("incorrect email");
};
const ClientModel = mongoose.model("client", userSchema, "users");
login controller:
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
res.redirect('/success');
} catch (err) {
console.log(err.message);
}
};
thanks in advance for your help, if you need more info please feel free to ask
Following #EAzevedo 's advice.
i just change my Controller
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
if (user.role == "client") {
res.redirect("/success");
} else if (user.role == "technicien") {
res.redirect("/success-technicien");
} else if (user.role == "superuser") {
res.redirect("/success-admin");
};
} catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
}
};
when you get the user , you should have field for the role ,
then check which role logged in and redirect him to where he needs to be
What's the proper way to validate old user password while updating new password?
So far i have tried and always get error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
What i did:
I tried using bcrypt to compare the old password from req.body with user existing password and then hash with bcrypt before saving. Comparing the password using bcrypt gave the error above. Not comparing the old password at all and just saving new password works properly.
My code:
exports.updatePassword = async (req, res) => {
try {
const { oldPassword, password } = req.body;
let updatedPassword = {
password: password,
};
const user = await User.findOneAndUpdate(
{ _id: req.params.userId },
{ $set: updatedPassword },
{ new: true, useFindAndModify: false }
);
// validate old password
bcrypt.compare(oldPassword, user.password, function (err, match) {
if (!match || err)
return res.status(400).send('Please enter correct old password');
});
//hash password and save user
bcrypt.genSalt(12, function (err, salt) {
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
user.save();
return res.json({user});
});
});
} catch (err) {
console.log(err);
return res.status(400).send('Something went wrong. Try again');
}
};
The issue is that the updatePassword function is ending before you actually process everything. To avoid nested function calls and returns, use the async methods provided by bcrypt (also check their recomendation on using async vs sync).
Regarding the code itself, you are updating the user's password before checking if the password is valid. You should get the user from the db, check if the current password matches, and only then insert the new hashed password into the db.
exports.updatePassword = async (req, res) => {
const { oldPassword, password } = req.body;
try {
// get user
const user = await User.findById(req.params.userId);
if (!user) {
return res.status(400).send('User not found');
}
// validate old password
const isValidPassword = await bcrypt.compare(oldPassword, user.password);
if (!isValidPassword) {
return res.status(400).send('Please enter correct old password');
}
// hash new password
const hashedPassword = await bcrypt.hash(password, 12);
// update user's password
user.password = hashedPassword;
const updatedUser = await user.save();
return res.json({ user: updatedUser });
} catch (err) {
console.log(err);
return res.status(500).send('Something went wrong. Try again');
}
};
i am currently building a React application with Nodejs/Express Backend and try to implement a Login Authentication.
i register Users with Name, email, password and hash the password with bcrypt:
router.post('/register', async (req, res) => {
// Hashing
const salt = await bcrypt.genSalt(10)
const hashedPassword = await bcrypt.hash(req.body.regpassword, salt)
// Validate
const {error} = Joi.validate(req.body, schema)
var data = {
regname: req.body.regname,
regemail: req.body.regemail,
regpassword : hashedPassword
}
var sql ='INSERT INTO Users (regname, regemail, regpassword) VALUES (?,?,?)'
var params =[data.regname, data.regemail, data.regpassword]
db.run(sql, params, function (err, result) {
if (error){
res.status(400).send(error.details[0].message);
return;
}
res.json({
"answer": "Success",
})
res.status(200)
});
})
this works fine. But my /login route doesnt work:
router.post('/login', (req, res, next) => {
let sql = `SELECT * FROM Users WHERE regname = "${req.body.regname}" AND regpassword = "${req.body.regpassword}"`;
var x;
db.all(sql, (err, rows) => {
if (err) {
next(err);
return;
}
if (!rows) {
res.status(400);
res.send('Invalid username or password');
return
}
rows.forEach( async (row) => {
if (row.regname === req.body.regname && await bcrypt.compare(req.body.regpassword, row.regpassword) ) {
x = 1;
}
else {
x = 2;
db.close();
}
})
if (x === 1) {
res.json({
"answer":"Success",
})
}
else {
res.json(
{"answer":"Denied",
})
}
})
})
The salt needs to be stored in the database as well.
The /login route must then retrieve the regpassword and the salt from the database based on the req.body.regname. It then needs to run a await bcrypt.hash(req.body.regpassword, salt) exactly identical to the /register route and then compare the result of that hashing operation with the regpassword from the database. If the two hashes match then the user provided the correct password and you can display some confirmation / issue some session token / ...
i share my solution here, if someone needs it:
router.post('/login', (req, res) => {
const regname = req.body.regname;
const regpassword = req.body.regpassword;
const findUserByName = (regname, cb) => {
return db.get(`SELECT * FROM Users WHERE regname = ?`,[regname], (err, row) => {
cb(err, row)
});
}
findUserByName(regname, (err, user)=>{
if (err) return res.status(500).send('Server error!');
if (!user) return res.status(404).send('User not found!');
const result = bcrypt.compareSync(regpassword, user.regpassword);
if(!result) return res.status(401).send('Password not valid!');
res.status(200)
res.json({
"answer":"Success",
})
});
});
I am trying to make a POST request that creates a new account. It first checks if there is already an account using that email. If there isn't, it then creates a new account and stores it in a user collection. It also does two things, it hashes the person's password and generates a token for that user when the account is created. Here is my code for my first attempt:
const bcrypt = require('bcryptjs');
const authentication = require('../controllers/authentication');
const express = require('express');
const passport = require('passport');
const passportService =require('../services/passport');
const config = require('../config/config');
const User = require('../model/user');
const router = express.Router();
const mongodb = require('mongodb');
const mongoose = require('mongoose');
mongoose.Promise = Promise;
router.post('/register', (req, res, next) => {
// I guess I will have to use this, and set this to true or false within findOne
let doesExist_ = undefined;
function generateUserToken(user){
return jwt.encode({sub: user._id, iat: timeStamp}, config.secretKey);
}
if (!req.body.email || !req.body.password) {
return res.status(442).send({error: 'Make sure that you entered your email and password'});
}
/// this doesn't return a boolean
User.findOne({email: req.body.email})
.then((err, user) => {
if (user !== null || user !== undefined){
doesExist_ = true;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
.then(() => {
console.log('in second Then, doesExist_ is ' + doesExist_);
if (!doesExist_){
let password = req.body.password;
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash){
if (err) throw err;
password = hash;
})
}).then(() => {
User.create({
username: req.body.username,
password: password,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
})
});
}
else {
return res.status(422).send({error: "Email is already in use"});
}
})
.catch((err) => {
console.log(err);
});
});
However, it isn't working, every time I try to create a new User, it fails to do so. Postman takes three minutes to process the request and when I check Robomongo afterwards, the user I just created is not in the collection.
I also wrote a second version of this making use of promises. I thought maybe the problem was that I needed to make my code wait for bcrypt to hash the password, then I could create and save the entry to the database:
const bcrypt = require('bcryptjs');
const authentication = require('../controllers/authentication');
const express = require('express');
const passport = require('passport');
const passportService =require('../services/passport');
const config = require('../config/config');
const User = require('../model/user');
const router = express.Router();
const mongodb = require('mongodb');
const mongoose = require('mongoose');
mongoose.Promise = Promise;
router.post('/register', (req, res, next) => {
// I guess I will have to use this, and set this to true or false within findOne
let doesExist_ = undefined;
function generateUserToken(user){
return jwt.encode({sub: user._id, iat: timeStamp}, config.secretKey);
}
if (!req.body.email || !req.body.password) {
return res.status(442).send({error: 'Make sure that you entered your email and password'});
}
/// this doesn't return a boolean
User.findOne({email: req.body.email})
.then((user) => {
/// this should be null if it doesn't exist
if (user !== null){
doesExist_ = true;
console.log('in first Then, user is...' + user);
console.log('in first Then, doesExist_ is...' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
.then(() => {
console.log('in second Then, doesExist_ is ' + doesExist_);
if (!doesExist_){
let password = req.body.password;
let hashedPassword;
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash){
if (err) throw err;
hashedPassword = hash;
console.log('hashedPassword is now...' + hashedPassword + ' and the hash is...' + hash);
})
});
let hashPromise = () => {
return new Promise ((resolve, reject) => {
if (hashedPassword !== undefined && hashedPassword !== null){
console.log('Within hashPromise, hashedPassword is...' + hashedPassword);
resolve();
}
else {
console.log('Within hashPromise, hashedPassword is...' + hashedPassword);
reject('hashedPassword is still undefined');
}
});
};
hashPromise().then(() => {
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
})
});
}
else {
return res.status(422).send({error: "Email is already in use"});
}
})
.catch((err) => {
console.log(err);
});
However, I am still running into the same problems with this version. It is running for a long ass time in Postman, and when the server times out, I still don't have the entry added to the collection. Am I chaining the promises correctly in this approach?
When I run this version I get this console log:
in first Then, doesExist_ is false
in second Then, doesExist_ is false
Within hashPromise, hashedPassword is...undefined
(node:8684) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): hashedPassword is still
undefined
hashedPassword is now...$2a$10$2VQ6AlaEgnrCvt8udshnZe/auswqawe8eZ1f5IXESmKmQVEI7nYNa and the hash is...$2a$10$2VQ6Al
aEgnrCvt8udshnZe/auswqawe8eZ1f5IXESmKmQVEI7nYNa
Before Bcrypt finishes hashing the password, I get the console.log from my Promise. Is the console.log an indicator that the Promise is not working? The Promise is supposed to be resolved once the variable hashedPassword is no longer undefined after bcrypt finishes hashing it. But it doesn't appear to be running as I intended.
Any tips as to how I can fix either versions? Am I doing too much in a single request given that I am also generating a token as well?
Any help would be greatly appreciated.
The standard bcrypt library does not return a promise - if you want to work with promises, you could use bcrypt-promise.
If you want to use the standard bcrypt lib, you'll have to work in the callback:
User.findOne({email: req.body.email})
.then((user) => {
if (user !== null){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash) {
// this is the function bcrypt calls when it's done
if (err) throw err;
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
});
});
});
}
});
(reduced example to show nesting - you'll still need to handle your errors and stuff)
alternatively - because callbacks get nested deep really quickly, you can of course create an extra function and give that to bcrypt as a callback:
var afterHash = function(err, hash){
if (err) throw err;
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
});
};
User.findOne({email: req.body.email})
.then((user) => {
if (user !== null){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, afterHash);
});
}
});
I am currently learning how to use mongoose and I did not get to learning about hashes yet but I glimpsed at the docs and I think you are not using the methods correctly ...
first of all mongo docs say that findone (queries) don't return a promise but have a then() function
I would try something like :
User.findOne({email: req.body.email}).exec()
.then((user) => {
/// this should be null if it doesn't exist
if (user !== null){
doesExist_ = true;
console.log('in first Then, user is...' + user);
console.log('in first Then, doesExist_ is...' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
or:
User.findOne({email: req.body.email})
.then((user) => {
doesExist_ = true;
console.log(user);
console.log('in first Then, doesExist_ is...' + doesExist_);
},(err) =>
{
doesExist_ = false;
console.log(err);
console.log('in first Then, doesExist_ is ' + doesExist_);
}
)
second of all bcrypt's genSalt() and hash() return a promise If the callbacks are omitted ...
Hope I was of help ...