Create USER with bcrypt and authenticate - node.js

This is my user.js file, where i handle two requestes.
First the POST /signup, where the user enters an email, and a password so that i can store it in mongodb.
var express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
var bcrypt = require("bcrypt");
var jwt = require("jsonwebtoken");
var User = require("../models/user");
router.post("/signup", (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length >= 1) {
return res.status(409).json({
message: "Mail exists"
});
} else {
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).json({
error: err
});
} else {
const user = new User({
_id: new mongoose.Types.ObjectId(),
email: req.body.email,
password: hash
});
user
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "User created"
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
}
});
}
});
});
Second the POST /login, where the user enters an email, and the password and with bcrypt i compare if the password matches the one in the db.
router.post("/login", (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length < 1) {
return res.status(401).json({
message: "Auth failed"
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed"
});
}
if (result) {
const token = jwt.sign(
{
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY,
{
expiresIn: "1h"
}
);
return res.status(200).json({
message: "Auth successful",
token: token
});
}
res.status(401).json({
message: "Auth failed"
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
The problem is the following: Whenever i try to create a user /signup using postman the request stays in "loading" and the server shuts down.
POSTMAN body: {
'emial': 'teste#gmail.com',
'password': '12345'
}
Error in the server:
UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [node-rest-shop-shard-00-01-pbcph.azure.mongodb.net:27017] on first connect [MongoNetworkError: connection 4 to node-rest-shop-shard-00-01-pbcph.azure.mongodb.net:27017 timed out]
(node:1496) 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: 1)
(node:1496) [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.

The User.find promise is being rejected because Node fails to connect to your MongoDB instance. This rejected promise tries to execute the first error handler that it comes across. In your case, it fails to find any catch() to handle this error. You can avoid this by adding a catch() to the promise chain at the end.
You should also look into why your Node instance cannot make a successful connection to your MongoDB instance.
router.post("/signup", (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length >= 1) {
return res.status(409).json({
message: "Mail exists"
});
} else {
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).json({
error: err
});
} else {
const user = new User({
_id: new mongoose.Types.ObjectId(),
email: req.body.email,
password: hash
});
user
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "User created"
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
}
});
}
}).catch(err => { //Handle the error here.
console.log(err);
res.status(500).json({
error: err
});
});
});
You should do the same for your login call.

Related

Node Express.js Middleware, end Response

validateRegister: async (req, res, next) => {
UserModel.findOne({email:req.body.email}, (err, example) => {
console.log(example);
if(err) console.log(err);
if(example) {
res.status(400).json({message: "Email already registered!"});
res.end() //next('route')
}
});
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
next();
},
Ok, I tried to insert user data if it is not already in the database using mongoose. If the User regarding the email is already in the database the response should be ended and the user not inserted. I tried to end the response with res.end() and next('route'), but nothing seems to work, the console.log("test") still runs.
Error:
events.js:353
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:561:11)
Thanks for your help
Code below callback function gets executed before callback gets completed and multiple res.send happened.
you can try this
validateRegister: async (req, res, next) => {
UserModel.findOne({ email: req.body.email }, (err, example) => {
console.log(example);
if (err) {
console.log(err);
return res.status(500).json({ message: "Something went wrong" });
}
if (example) {
return res.status(400).json({ message: "Email already registered!" });
}
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
});
next();
}
Or
validateRegister: async (req, res, next) => {
try {
let example = await UserModel.findOne({ email: req.body.email });
console.log(example);
if (example)
return res.status(400).json({ message: "Email already registered!" });
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
next();
} catch (err) {
console.log(err);
return res.status(500).json({ message: "Something went wrong" });
}
}
you can add return before returning response in the case of user email already found.
What seems to happen is that your program is calling res two times

How to hash two password simultaneously using bcyrpt?

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.

nodejs 500 internal server error on production server

I was testing my app on localhost and everything seemed to work correctly, but then I pushed my website to azure websites and things were working there as well except when I create a user it throws 500 error I don't get what could be the possible reason.
here is my route for creating users
router.post('/create', admin, (req, res, next) => {
bcrypt.hash(req.body.password, 10).then(hash => {
const user = new User({
email: req.body.email,
username: req.body.username,
userType: req.body.userType,
company: req.body.company
});
User.countDocuments({
email: req.body.email
})
.then(count => {
if (!count) {
user.save()
.then(result => {
res.status(201).json({
message: 'User created!',
result: result
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
} else {
res.status(500).json({
message: 'Email already exists!'
});
}
})
.catch(err => {
res.status(500).json({
error: err,
message: 'Email already exists!'
});
});
});
});
so whenever I test the app on angular it shows email already exists! and 500 error

Keep getting "Can't set headers after they are sent" using Node/Express

I keep getting "Can't set headers after they are sent" building a Node/Express API.
The issue is I am not setting the headers after the response has been sent anywhere. I am always calling res.status(xxx).json({}) to close ever condition.
Route
const router = require('express').Router();
router.get('/password/validate/:hash', PasswordController.validate);
router.post('/password/update', PasswordController.update);
Controller
This is where the error is occurring. I am calling the validate request specifically.
// Import node packages
const mongoose = require('mongoose');
const Password = require('../models/password');
const User = require('../models/user');
const bcrypt = require('bcryptjs');
const moment = require('moment');
const string = require('../middleware/string_functions')
exports.update = (req, res, next) => {
User.findOne({ email: req.body.email })
.exec()
.then(user => {
if (!user) {
res.status(401).json({
message: 'Cannot retrieve account'
})
}
const expiry = moment().add(30, 'seconds');
const unique_string = string.generate_random(32);
const url_hash = string.base64_encode(unique_string +':'+ user._id);
bcrypt.hash(unique_string, 10, (err, hash) => {
if (err) {
res.status(500).json({
error: err.message
})
}
const query = { user_id: user._id }
const newData = {
hash,
expiry
}
Password.findOneAndUpdate(query, newData, { upsert: true, new: true })
.exec()
.then(request => {
res.status(201).json({
message: 'success',
url: 'localhost:8081/users/password/validate/' + url_hash,
data: request
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
}
exports.validate = (req, res, next) => {
if (!req.params.hash) {
res.status(500).json({
error: 'Missing hash'
})
}
const data = string.base64_decode(req.params.hash).split(':');
console.log(data)
Password.findOne({ user_id: data[1] })
.exec()
.then(request => {
if (!request) {
res.status(404).json({
message: 'Change request not found or expired'
})
}
bcrypt.compare( data[0], request.hash, (err, result) => {
if (err) {
res.status(500).json({
error: err.message
})
}
if (result) {
if (moment().isAfter(request.expiry)) {
res.status(401).json({
message: 'Time has expired'
})
}
res.status(200).json({
message: 'Hash validation successful'
})
}
res.status(500).json({
error: 'Something went wrong'
})
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
}
Console Error
_http_outgoing.js:494
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
at ServerResponse.header (/Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/express/lib/response.js:267:15)
at bcrypt.compare (/Users/chrislloyd/Development/Projects/happy-hour-api/api/controllers/passwords.js:83:22)
at /Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/bcryptjs/dist/bcrypt.js:297:21
at /Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/bcryptjs/dist/bcrypt.js:1353:21
at Immediate.next [as _onImmediate] (/Users/chrislloyd/Development/Projects/happy-hour-api/node_modules/bcryptjs/dist/bcrypt.js:1233:21)
at runCallback (timers.js:789:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
Updated Example
exports.update = (req, res, next) => {
// Check if hash value exists
if (!req.params.hash) {
res.status(500).json({
error: 'Missing hash value'
});
return;
}
// Check if password and confirmation are the same
if (req.body.password != req.body.passwordConfirmation) {
res.status(401).json({
message: 'Password confirmation does not match'
});
return;
}
// Decode and split hash and user id into array
const data = string.base64_decode(req.params.hash).split(':');
// Find record that contains user id
Password.findOne({ user_id: data[1] })
.exec()
.then(request => {
console.log(request)
// Throw 404 error if record is not found
if (!request) {
return res.status(404).json({
message: 'Password change request doest not exist or timed out'
});
}
// Check if change request has expired
if (moment().isAfter(request.expiry)) {
res.status(401).json({
message: 'Password change request expired',
request: {
request: 'http://localhost:3001/users/password/request'
}
});
// Delete expired record
Password.remove({ _id: request._id })
.exec()
.catch(err => {
res.status(500).json({
error: err.message
});
});
return;
}
// Compare hash value from encoded string to encrypted hash value in database
console.log(mongoose.Types.ObjectId(request.user_id))
bcrypt.compare( data[0], request.hash, (err, result) => {
// Bcrypt error performing comparison
if (err) {
res.status(500).json({
error: err.message
});
return;
}
// Check if result is true
if (result) {
// Find user record matching request.user_id and update password
User.findOneAndUpdate({ _id: mongoose.Types.ObjectId(request.user_id) }, {$set: { password: req.body.password }}, {new: true}, (err, user) => {
console.log(user)
// Error finding and updating user record
if (err) {
res.status(500).json({
error: err.message
});
return;
}
// If returned user account is not null
if (user) {
res.status(200).json({
message: 'Password updated',
user
});
return;
}
// Could not find user record
res.status(404).json({
message: 'Could not find user account to update'
});
return;
})
}
// Catch all error
res.status(500).json({
error: 'Something went wrong'
});
return;
})
})
.catch(err => {
res.status(500).json({
error: err.message
});
return;
});
}
That particular error is caused when you send multiple responses to the same request.
You see to be thinking that as soon as you do res.status(...).json(...) that your function returns and stops executing. It does not. res.json() is just a regular function call. It doesn't change the control flow in your function at all (unless it throws an exception). A successful call to res.json() executes and then your function just keeps right on executing the lines of code that follow.
What you need is a return statement after each time you send a response (if there is any other code in your function that could execute and send another response) so that your function doesn't continue to execute and send another response or you could bracket your responses in if/else statements so you don't execute the sending of more than one response.
Here's a fixed version with 5 added return statements to keep the rest of your code from executing after you've sent a response and to keep you from sending multiple responses to the same request. Each addition is commented with ==> added:
// Import node packages
const mongoose = require('mongoose');
const Password = require('../models/password');
const User = require('../models/user');
const bcrypt = require('bcryptjs');
const moment = require('moment');
const string = require('../middleware/string_functions')
exports.update = (req, res, next) => {
User.findOne({ email: req.body.email })
.exec()
.then(user => {
if (!user) {
res.status(401).json({
message: 'Cannot retrieve account'
})
return; // <== added
}
const expiry = moment().add(30, 'seconds');
const unique_string = string.generate_random(32);
const url_hash = string.base64_encode(unique_string +':'+ user._id);
bcrypt.hash(unique_string, 10, (err, hash) => {
if (err) {
res.status(500).json({
error: err.message
})
return; // <== added
}
const query = { user_id: user._id }
const newData = {
hash,
expiry
}
Password.findOneAndUpdate(query, newData, { upsert: true, new: true })
.exec()
.then(request => {
res.status(201).json({
message: 'success',
url: 'localhost:8081/users/password/validate/' + url_hash,
data: request
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
}
exports.validate = (req, res, next) => {
if (!req.params.hash) {
res.status(500).json({
error: 'Missing hash'
})
}
const data = string.base64_decode(req.params.hash).split(':');
console.log(data)
Password.findOne({ user_id: data[1] })
.exec()
.then(request => {
if (!request) {
res.status(404).json({
message: 'Change request not found or expired'
})
return; // <== added
}
bcrypt.compare( data[0], request.hash, (err, result) => {
if (err) {
res.status(500).json({
error: err.message
})
return; // <== added
}
if (result) {
if (moment().isAfter(request.expiry)) {
res.status(401).json({
message: 'Time has expired'
})
}
res.status(200).json({
message: 'Hash validation successful'
})
return; // <== added
}
res.status(500).json({
error: 'Something went wrong'
})
})
})
.catch(err => {
res.status(500).json({
error: err.message
})
})
}
The res object by itself does not stop the execution of your program. You must use return if you prefer to use Guard Clauses instead of Nested Conditions
Replace statements like this:
if (err) {
res.status(500).json({
error: err.message
})
}
With this:
if (err) {
res.status(500).json({
error: err.message
});
return; // return statement added
}

Unhandled promise rejection - Error: Can't set headers after they are sent

I am new to node, and I have a simple situation, where I am posting to an endpoint on a node/express app. The issue is that I get:
POST /api/v2/user 500 25.378 ms - 54
(node:19024) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Can't set headers after they are sent.
(node:19024) 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.
The relevant code that I have which is generating this is:
router.post('/', (req, res, next) => {
return authHelpers.createUser(req, res)
.then((user) => {
return localAuth.encodeToken(user[0]);
})
.then((token) => {
res.status(201).json({
status: 'success',
message: 'User Created',
token: token
});
})
.catch((err) => {
res.status(500).json({
status: 'error'
});
});
});
and then:
function createUser(req, res) {
return handleErrors(req)
.then(() => {
const salt = bcrypt.genSaltSync();
const hash = bcrypt.hashSync(req.body.password, salt);
return knex('users')
.insert({
email: req.body.email,
first_name: req.body.first_name,
last_name: req.body.last_name,
username: req.body.username,
password: hash
})
.returning('*');
})
.catch((err) => {
res.status(410).json({
status: err.message
});
});
}
function handleErrors(req) {
return new Promise((resolve, reject) => {
if (req.body.username.length < 6) {
reject({
message: 'Username must be longer than 6 characters'
});
} else if (req.body.password.length < 6) {
reject({
message: 'Password must be longer than 6 characters'
});
} else {
resolve();
}
});
}
I do know that if I remove the res.status(500).json({status: 'error'}); specifically, then the error goes away, but I am not sure if that is proper.
Any clue to what exactly is my error and how to fix?
You are trying to send response twice. First when catching the error
res.status(410).json({
status: err.message
});
And then after catch, promise chain continues the normal route until:
return localAuth.encodeToken(user[0]);
Which fails, because user is undefined and throws an exception.. so error handler is called and you are trying to send response again, but it fails because it has already been sent once
res.status(500).json({
status: 'error'
});
console log which error was thrown in the last part, I'm pretty sure it is something like
TypeError: Cannot read property '0' of undefined

Resources