How to hash two password simultaneously using bcyrpt? - node.js

I am trying to get login page with two passwords. For only one password the code is working perfectly but when I am adding another password it is throwing error "parallel Save Error".
[0] (node:16516) UnhandledPromiseRejectionWarning: ParallelSaveError: Can't save() the same
doc multiple times in parallel. Document: 5e703180c90fbc40848fcfca
[0] (node:16516) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated
either by throwing inside of an async function without a catch block, or by rejecting a promise which
was not handled with .catch(). (rejection id: 2)
[0] (node:16516) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated.
In the future, promise rejections that are not handled will terminate the Node.js process with a non-
zero exit code.
This is my user.js script:
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const config = require('config');
const jwt = require('jsonwebtoken');
const User = require('../../models/User');
router.post('/', (req, res,) => {
const { name, email, password1, password2 } = req.body;
if(!name || !email || !password1 || !password2) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
User.findOne({ email })
.then(user => {
if(user) return res.status(400).json({ msg: 'User already exists' });
const newUser = new User({
name,
email,
password1,
password2
});
// Hash
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password1, salt , (err, hash) => {
if(err) throw err;
newUser.password1 = hash;
newUser.save()
.then(user => {
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if(err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
)
});
})
})
// Create salt & hash
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password2, salt, (err, hash) => {
if(err) throw err;
newUser.password2 = hash;
newUser.save()
.then(user => {
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if(err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
)
});
})
})
})
});
module.exports = router;
and the following is the code for Authentication.js
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const config = require('config');
const jwt = require('jsonwebtoken');
const auth = require('../../middleware/auth');
// User Model
const User = require('../../models/User');
router.post('/', (req, res) => {
const { email, password1, password2 } = req.body;
// for validation
if(!email || !password1 || !password2) {
return res.status(400).json({ msg: 'Please enter all fields' });
}
// Check if user exists
User.findOne({ email })
.then(user => {
if(!user) return res.status(400).json({ msg: 'User Does not exist' });
// Validation of password
bcrypt.compare(password1, user.password1)
.then(isMatch => {
if(!isMatch) return res.status(400).json({ msg: 'Invalid credentials' });
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if(err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
)
})
bcrypt.compare(password2, user.password2)
.then(isMatch => {
if(!isMatch) return res.status(400).json({ msg: 'Invalid credentials' });
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if(err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email
}
});
}
)
})
})
})
router.get('/user', auth, (req, res) => {
User.findById(req.user.id)
.select('-password1, -password2')
.then(user => res.json(user));
});
module.exports = router;
I am getting only password2 as a hashed password.
password1:"ddd"
password2:"$2a$10$PQhBiDtelKoRspAFn7BW0OuI0pnAyDl.DQSag6bBvYdlirBZM/oAq"
what should I need to do to remove these errors?

I got the answer of above problem...
just need to change the following part in user.js. no need to create hash twice.
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password1, salt , async (err, hash) => {
bcrypt.hash(newUser.password2, salt, async (err, hash) => {
if(err) throw err;
newUser.password1 = hash;
newUser.password2 = hash;
await newUser.save()
...
...
same need to do in the athentication.js script.
bcrypt.compare(password1, user.password1)
bcrypt.compare(password2, user.password2)
.then(isMatch => {
if(!isMatch) return res.status(400).json({ msg: 'Invalid credentials'
});
jwt.sign(
{ id: user.id },
...
...

I think it's better to write the encrypt password method in the model.js and export it with the User to the route.js or whatever its name for better & clean code so you can use your method as long as you need.

Related

I am using bcryptjs to hash my password so I got an error but in catch(err) don't give me any error

this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
this is the controller and if I do it whitout hashing it works but with hashing it give me an error and I cannot handle in catch(err)
const { throws } = require("assert");
const { bcrypt } = require("bcryptjs");
const User = require("../models/user");
// #desc register new user
// #routes POST /users/register
exports.createUser = async (req, res) => {
const errors = [];
try {
await User.userValidation(req.body);
const { fullname, email, password } = req.body;
const user = await User.findOne({ email });
if (user) {
errors.push({ message: "کاربری با این ایمیل موجود است" });
return res.render("register", {
pageTittle: "صحفه ثبت نام",
path: "/register",
errors: errors,
});
}
bcrypt.genSalt(10, (err, salt) => {
if (err) throw err;
console.log(err);
bcrypt.hash(password, salt, async (err, hash) => {
if (err) throw err;
await User.create({
fullname,
email,
password: hash,
});
res.redirect("/users/login");
});
});
} catch (err) {
err.inner.forEach((e) => {
errors.push({
name: e.path,
message: e.message,
});
});
}
return res.render("register", {
pageTittle: "صحفه ثبت نام",
path: "/register",
errors: errors,
});
};

How to implement Node.JS model using Sequelize

I have a project with the below Model, Controller, and Route File for user. I am wanting to implement sequelize, which I have managed to partially achieve using the account files below, however, I am struggling to work out how to implement logging in and ensuring a request has a valid token usijng user.ensureToken which would become account.ensureToken.
I'm fairly new to Node.Js so I'm not even sure where to start
user.model.js
const bcrypt = require('bcrypt');
const sql = require("./db.js");
const HashBits = require("../config/auth.config.js");
const faker = require('faker');
// constructor
const User = function(user) {
this.first_name = user.first_name;
this.last_name = user.last_name;
this.mob_no = user.mob_no;
this.user_name = user.user_name;
this.password = user.password;
};
User.create = (newUser, result) => {
bcrypt.hash(newUser.password, HashBits.saltRounds, (err, hash) => {
newUser.password = hash;
sql.query("INSERT INTO users SET ?", newUser, (err, res) => {
if (err) {
// console.log("error: ", err);
result(err, null);
return;
}
newID = res.insertId;
// console.log("created user: ", { id: res.insertId, ...newUser });
result(null, { id: res.insertId, ...newUser });
});
});
};
User.authenticate = (user,result) => {
// sql.query(`SELECT * FROM customers WHERE id = ${customerId}`, (err, res) => {
sql.query(`SELECT * FROM users WHERE user_name = '${user.user_name}'`, (err, res) => {
// sql.query("SELECT * FROM users ", (err, res) => {
if (err) {
// console.log("error: ", err);
result(err, null);
return;
}
if(res.length !== 1){
// console.log("error: found multiple users");
result("error: found multiple users", null);
return;
}
// console.log("Found user: ",res[0]);
bcrypt.compare(user.password, res[0].password, function(err, res2) {
if(res2){
// console.log("Yes");
result(null,res[0]);
}else{
// console.log("On ya bike");
result("ERROR",null);
// return;
}
});
});
};
module.exports = User;
user.controller.js
const User = require("../models/user.model.js");
var jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
// Create and Save a new User
exports.create = (req, res) => {
// Validate request
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
// Create a User
const user = new User({
first_name: req.body.first_name,
last_name: req.body.last_name,
mob_no: req.body.mob_no,
user_name: req.body.user_name,
password: req.body.password
});
// Save User in the database
User.create(user, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while creating the User."
});
else res.send(data);
});
};
exports.authenticate = (req,res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
const user = new User({
user_name: req.body.user_name,
password: req.body.password
});
User.authenticate(user, (err,data) => {
if(err)
res.status(500).send({
message:
err.message || "Some error occurred while authenticating the User."
});
else {
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
// res.send(data);
res.status(200).send({
id: data.id,
username: data.user_name,
accessToken: token
});
}
});
};
exports.ensureToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!"
});
}
req.userId = decoded.id;
next();
});
}
user.routes.js
module.exports = app => {
const users = require("../controllers/user.controller.js");
// Create a new User
app.post("/User", users.create);
// Login
app.post("/User/Login", users.authenticate);
};
account.model.js
const bcrypt = require("bcrypt");
module.exports = (sequelize, Sequelize) => {
const Account = sequelize.define("account", {
firstName: {
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING,
set(value){
const hash = bcrypt.hashSync(value, 10);
this.setDataValue('password', hash);
}
}
});
return Account;
};
account.controller.js
const db = require("../models");
const Account = db.accounts;
const Op = db.Sequelize.Op;
var jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
// Create and Save a new New
exports.create = (req, res) => {
// Validate request
if (!req.body.username) {
res.status(400).send({
message: "Content can not be empty!"
});
return;
}
// Create a Site
const account = {
firstName: req.body.firstName,
username: req.body.username,
password: req.body.password
};
Account.create(account)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Account."
});
});
};
exports.authenticate = (req,res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
const account = new Account({
username: req.body.username,
password: req.body.password
});
};
account.routes.js
module.exports = app => {
const accounts = require("../controllers/account.controller.js");
var router = require("express").Router();
app.post("/account", accounts.create);
app.post("/account/Login", accounts.authenticate);
};
you need to use jwt token for access token as you said and you are bcrypt password in model file which will be security issue you have to bcrypt password as soon as it comes in request I have implemented it in my answer remove code of password bcrypt from your model file
you have to import in your account.controller.js
const db = require("../models");
const User = db.user;
require('dotenv').config();
const Op = db.Sequelize.Op;
const errors = require('../config/errors');
const error = errors.errors;
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
module.exports = {
signup: async (req, res) => {
if (!req.body.first_name|| !req.body.lastt_name || !req.body.password) {
return res.status(200).send(error.MANDATORY_FIELDS);
}
try {
// Save User to Database
User.create({
name: req.body.name,
email: req.body.email,
mo_no: req.body.mo_no,
city: req.body.city,
password: bcrypt.hashSync(req.body.password, 8),
user_type: "admin"
}).then(function (user) {
return res.status(200).send(error.OK)
})
.catch(function (err) {
console.log(err);
return res.status(500).send(error.SERVER_ERROR)
});
} catch (e) {
console.log(e);
return res.status(500).send(error.SERVER_ERROR)
}
},
signin: async (req, res) => {
if (!req.body.email || !req.body.password) {
return res.status(200).send(error.MANDATORY_FIELDS);
}
User.findOne({
where: {
email: req.body.email
}
}).then(function (user) {
if (!user) {
return res.status(404).send(error.USER_NOT_PRESENT);
}
const passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(422).send(error.PASSWORD_MISSMATCH, {
accessToken: null
});
}
const token = jwt.sign({ id: user.id, first_name: user.first_name },
process.env.secret, {
expiresIn: 86400 // 24 hours
});
const authorities = [];
return res.status(200).send({
id: user.id,
name: user.name,
email: user.email,
accessToken: token
});
});
})
.catch(function (err) {
console.log(err)
return res.status(500).send(error.SERVER_ERROR);
});
}
}
than you have to create a separate folder for authorization like authorize.js or authJwt.js where you have to check is token is valid or not put this code in authorize.js
at decoding time secret token or password also needed which you have in .env file
const jwt = require("jsonwebtoken");
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send(error.TOKEN_NOT_PROVIDED);
}
jwt.verify(token, process.env.secret, (err, decoded) => {
if (err) {
return res.status(401).send(error.UNAUTHORIZED);
}
req.first_name= decoded.first_name;
req.id = decoded.user_id
next();
});
};
const authJwt = {
verifyToken: verifyToken
};
module.exports = authJwt;
than you have to import authorize.js file in your routes whenever you want like this
const authorize = require('../authorize.js');
module.exports = app => {
const accounts = require("../controllers/account.controller.js");
var router = require("express").Router();
app.post("/account", accounts.create);
app.post("/account/Login",
authorize.verifyToken,accounts.authenticate);
};
it will be more effective if you genreate access token at signin time

error: data and hash arguments required for bcrypt. Incorrect MongoDB setup

I am trying to set up Passport with Express and MongoDB. At the moment I am able to register users in the database. But whenever I try to login, I get an error saying that data and hash arguments are required. Right now I have my Server.js file like this
const mongoose = require('mongoose');
const User = require('./models/users')
const initializePassport = require('./passport-config')
initializePassport(
passport,
email => User.find({email: email}),
id => User.find({id: id})
)
app.post('/register', checkNotAuthenticated, async (req, res) => {
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10)
const newUser = new User({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
res.redirect('/login')
console.log(newUser)
} catch {
res.redirect('/register')
}
And my Passport-Config.js file like this `
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt');
const User = require('./models/users')
function initialize(passport, getUserByEmail, getUserById) {
const authenticateUser = async (email, password, done) => {
const user = getUserByEmail(email)
if (user === null) {
return done(null, false, { message: 'No user with that email' })
}
try {
if (await bcrypt.compare(password, user.password)) {
return done(null, user)
} else {
return done(null, false, { message: 'Password incorrect' })
}
} catch (e) {
return done(e)
}
}
passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser))
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser((id, done) => {
return done(null, User.findById({user: id}))
})
}
`
I've done some investigation using console.log() statements (not proud of it) but I think I've managed to find out the issue. If we add in the the first console log statement here:
app.post('/register', checkNotAuthenticated, async (req, res) => {
try {
console.log("BCRYPT COMPARE RUNS HERE")
const hashedPassword = await bcrypt.hash(req.body.password, 10)
const newUser = new User({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
res.redirect('/login')
console.log(newUser)
} catch {
res.redirect('/register')
}
and the second one here:
const initializePassport = require('./passport-config')
initializePassport(
passport,
email => User.find({email: email}).then((result) => { console.log("USER DATA EXTRACTED HERE") }).catch((err) => { console.log(err) }),
id => User.find({id: id})
)
The next time you click on login, you should see an output like:
Listening on port 3000
BCRYPT COMPARE HAPPENING
Error: data and hash arguments required
...
...
...
USER DATA EXTRACTED HERE
Notice that bcrypt.compare is being run before we are actually able to grab the user information from the DB? This means that all the arguments into that function are null, which is what is returning that error. Now, I'm no JS expert, but this can be fixed with an await statement added here:
function initialize(passport, getUserByEmail, getUserById) {
const authenticateUser = async (email, password, done) => {
const user = await getUserByEmail(email)
if (user === null) {
return done(null, false, { message: 'No user with that email' })
}
Which makes sure that the user info is queried from the DB before moving along in the script.

Why can't I authenitcate a registered user for this simple MEAN app?

First of all, let me give you a warm thank you for giving a thought to this question.
So, what's the problem?
(This is a simple problem for most of you grandmasters!)
Well, the user can be registered to this simple app. But, for some reason, authentication doesn't work. That some reason is what my brain nerves having a hard time comprehending!
Having tried all the possible solutions for hours and hours, this novice-newbie decided to head over to the haven of veterans here in the StackOverflow!
Let me give you the code, so you can shed me some bright light!
Following is a capture of the code written for the authentication
//Authenticating the user
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
User.getUserByUsername(username, (err, user) => {
if (err) throw err;
if (!user) {
return res.json({
sucess: false,
msg: 'There is no such user found here'
});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
success: true,
token: 'JWT' + token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({
success: false,
msg: 'Enter the correct details!'
});
}
});
});
});
//Getting into the dashboard
router.get('/profile', passport.authenticate('jwt', {
session: false
}),
(req, res, next) => {
res.json({
user: req.user
});
});
The next few pictures on your way shows you the POSTMAN requests that are done by this novice.
Here, a post request is done to register the user and as you can see, there's not a smidgen of a problem there; the user is, without a doubt, registered!
Here's the authentication done with POSTMAN
But now, for some reason (which I have zero clue of), the user is NOT authenticated. This is the problem that I need to solve.
Here is a code of the model/user.js file in case you want to know what's in there as well
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config = require('../config/database');
// These are the collection or entities in ERD language
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
const User = module.exports = mongoose.model('user', UserSchema); //User is the name give for this particular model's schema
// these are functions implemented to do a certain task
module.exports.getUserById = function (id, callback) {
User.findById(id, callback);
}
module.exports.getUserByUsername = function (username, callback) {
const query = {
username: username
}
User.findOne(query, callback);
}
module.exports.addUser = function (newUser, callback) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save(callback);
});
})
}
//comparing the hash password
module.exports.comparePassword = function (candidatePasword, hash, callback) {
bcrypt.compare(candidatePasword, hash, (err, isMatch) => {
if (err) throw err;
callback(null, isMatch);
});
}
Thank You for your time!
Stay safe btw!
edit1: The code for the registration or signing up.
router.post('/signup', (req, res, next) => {
let newUser = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
//console.log("registration is working");
if (err) {
res.json({
sucess: false,
msg: 'Hey! Enter the correct information man!'
});
} else {
res.json({
success: true,
msg: 'you are registered'
});
}
});
});
Here's the whole routes/users.js file for you to refer
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
const config = require('../config/database');
// Signingup the user
router.post('/signup', (req, res, next) => {
let newUser = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
//console.log("registration is working");
if (err) {
res.json({
sucess: false,
msg: 'Hey! Enter the correct information man!'
});
} else {
res.json({
success: true,
msg: 'you are registered'
});
}
});
});
//Authenticating the user
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
User.getUserByUsername(username, (err, user) => {
if (err) throw err;
if (!user) {
return res.json({
sucess: false,
msg: 'There is no such user found here'
});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
success: true,
token: 'JWT' + token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({
success: false,
msg: 'Enter the correct details!'
});
}
});
});
});
//Getting into the dashboard
router.get('/profile', passport.authenticate('jwt', {
session: false
}),
(req, res, next) => {
res.json({
user: req.user
});
});
router.post('/login', (req, res, next) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
if (err) {
res.json({
success: false,
msg: "Enter the correct information"
});
} else {
res.json({
success: true,
msg: "User loggedIn"
});
}
});
});
module.exports = router;
In your schema, you don't have the field username but your query is {username: username}. That's why you can't find any user match and get the response "There is no such user found here". Change your query to {name: username} may solve the problem.

can not register a new user

Back end i wrote in node.js and front end in react.js
I want to register a new User to my system.
Register worked if I used Postman, but register not worked at back end
Register.js(react.js):
axios.defaults.baseURL = 'http://localhost:5040';
axios.post('/users/register', newUser)
.then(res => console.log(res.data))
.catch(err => console.log(err.response.data));
newUser = {name: "test", email: "test0991#gmail.ru", password: "123456", password2: "123456"}
Back end:
router.post('/register', (req, res) => {
const { errors, isValid } = validateRegisterInput(req.body);
// Check Validation
if(!isValid){
return res.status(400).json(errors);
}
User.findOne({ email: req.body.email })
.then(user => {
if(user){
*
} else {
*
*
*
);
const newUser = new User({
*
*
*
*
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) =>{
if(err) throw err;
newUser.password = hash;
newUser.save()
.then(user => res.json(user))
.catch(err => console.log(err));
});
});
}
});
});
Error:
Unhandled Rejection (TypeError): Cannot read property 'data' of undefined
console.log(newUser);
axios.post('/users/register', newUser)
.then(res => console.log(res.data))
.catch(err => console.log(err.res.data));
Perhaps you are not sending data. For some reason you use newUser when calling axios.post(), but you don't assign newUser a value until after calling axios.post(). The order should be swapped, like this:
axios.defaults.baseURL = 'http://localhost:5040';
newUser = {name: "test", email: "test0991#gmail.ru", password: "123456", password2: "123456"}
axios.post('/users/register', newUser)
.then(res => console.log(res.data))
.catch(err => console.log(err.response.data));

Resources