(node:13612) UnhandledPromiseRejectionWarning: ReferenceError: user is not defined - node.js

I'm trying to login using the id from mongoDB but nodejs keeps giving me this error about user...
(node:13612) UnhandledPromiseRejectionWarning: ReferenceError: user is not defined
registering the user is alright but when calling the login ..... it crashs although everything is defined and I can't figure out why its not reading user from the database
...
const express = require("express");
const { findOne } = require("../models/User");
const router = express.Router();
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const { registerValidation, loginValidation } = require("../validation");
const jwt = require("jsonwebtoken");
router.post("/register", async (req, res) => {
//VALIDATE DATA
const { error } = registerValidation(req.body);
if (error) return res.status(400).send(error.details[0].message);
//CHECK IF USER EXISTS
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) return res.status(400).send("Email already exists!");
//HASH THE PASSWORD
const salt = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(req.body.password, salt);
//CREATE NEW USER
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashPassword,
});
try {
const savedUser = await user.save();
res.send({user:user._id});
} catch (error) {
res.status(400).send(error);
}
});
router.post("/login", async (req, res) => {
//VALIDATE DATA
const { error } = loginValidation(req.body);
if (error) return res.status(400).send(error.details[0].message);
//CHECK IF USER EXISTS
const emailExist = await User.findOne({ email: req.body.email });
if (!emailExist) return res.status(400).send("Email or Password is Wrong");
//CREATE AND ASSIGN TOKEN
const token = jwt.sign({ _id: user._id }, process.env.TOKEN_SECRET);
res.header("auth-token", token).send(token);
});
module.exports = router;

The issue is an undefined variable:
const token = jwt.sign({ _id: user._id }, process.env.TOKEN_SECRET);
The user (user._id) variable in the line above isn't defined in the Login route, perhaps you meant to find the user first, then to assign the JWT Token?

Related

Not getting req.body results in login function despite it working in the registration function

I'm currently in the process of adding authentication to my app using the MERN stack.
I've managed to add the register functionality for the backend, but I'm struggling with the login function. Despite the user existing in the database, when I test it out in Postman I get an error every time. I tried to just find the user and not validate anything, but that also throws an error, which is confusing since the email definitely exists in the database.
Here's the loginUser and registerUser functions:
const jwt = require('jsonwebtoken');
const bcrypt = require ('bcryptjs')
const asyncHandler = require('express-async-handler')
let User = require('../models/user.model')
const registerUser = asyncHandler(async (req, res) => {
const {name, email, password} = req.body
//validate
if(!name || !email || !password) {
res.status(400)
throw new Error("Please add all fields")
}
const userExists = await User.findOne({email})
if(userExists){
res.status(400)
throw new Error("User already exists")
}
//hash password
const salt = await bcrypt.genSalt(10)
const hashedPassword = await bcrypt.hash(password, salt)
//create new user
const user = await User.create({
name : name,
email : email,
password : hashedPassword
})
if (user){
res.status(201).json({
_id: user.id,
name: user.name,
email: user.email
})
} else {
res.status(400)
throw new Error("Invalid user data")
}
})
const loginUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
// Check for user email
const user = await User.findOne({ email })
if (user && (await bcrypt.compare(password, user.password))) {
res.json({
_id: user.id,
name: user.name,
email: user.email
})
} else {
res.status(400)
throw new Error('Invalid credentials')
}
})
const getUser = asyncHandler(async (req, res) => {
res.json("getUser")
})
router.route('/').post(registerUser)
router.route('/login').post(loginUser)
module.exports = router
And the Postman request:
I double checked the spellings and routes, which are all working fine, and I simply can't put my finger on why it isn't finding the User.
Any direction would be appreciated!

Server can not perform JWT Authentication Request on MERN

I was working on a MERN app and was trying to use jason web-token (JWT) and bcrypt for authentication. It uses two routes for signup and authentication. However, I'm getting an internal server error coming from admin route not sure though. I have not been able to connect to the frontend correctly because of that.
//admin model
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const Joi = require('joi');
const passwordComplexity = require('joi-password-complexity');
const AdminSchema = new mongoose.Schema({
name: { type: String, required:true },
email: { type: String, required:true, unique:true },
password: { type: String, required:true, },
});
AdminSchema.methods.generateAuthToken = function(){
const token=jwt.sign({_id:this._id}, process.env.JWTPRIVATEKEY,{expiresIn: "7d",});
return token;
}
const AdminModel = mongoose.model("admin", AdminSchema)
const validate=(data) =>{
const schema= Joi.object({
name:Joi.string().required().label("Name"),
email:Joi.string().email().required.label("Email"),
password:passwordComplexity().required().label("Password")
});
return schema.validate(data)
};
module.exports = {AdminModel, validate};
//module.exports = AdminModel;
//admin route
const router= require("express").Router();
const {AdminModel, validate} = require("../models/admin");
const bcrypt = require("bcrypt");
router.post("/", async(req,res) =>{
try {
const {error} = validate(req.body);
if(error)
return res.status(400).send({message: error.details[0].message});
const user = await AdminModel.findOne({email: req.body.email});
if(user)
return res.status(409).send({message:"Admin with given email exists"});
const salt = await bcrypt.genSalt(Number(process.env.SALT));
const hashPassword = await bcrypt.hash(req.body.password, salt);
await new AdminModel({ ...req.body, password:hashPassword}).save();
res.status(201).send({message:"Admin created successfully"})
} catch (error) {
res.status(500).send({message:"Internal Server Error YOu see"})
}
})
module.exports = router;
//adminAuthentication route
const router = require("express").Router();
const {AdminModel} = require("../models/admin");
const bcrypt = require("bcrypt");
const Joi = require("joi");
router.post("/", async (req, res) => {
try {
const { error } = validate(req.body);
if (error)
return res.status(400).send({ message: error.details[0].message });
const Admin = await AdminModel.findOne({ email: req.body.email });
if (!Admin)
return res.status(401).send({ message: "Invalid Email or Password" });
const validPassword = await bcrypt.compare(
req.body.password,
Admin.password
);
if (!validPassword)
return res.status(401).send({ message: "Invalid Email or Password" });
const token = Admin.generateAuthToken();
res.status(200).send({ data: token, message: "logged in successfully" });
} catch (error) {
res.status(500).send({ message: "Internal Server Error" });
}
});
const validate = (data) => {
const schema = Joi.object({
email: Joi.string().email().required().label("Email"),
password: Joi.string().required().label("Password"),
});
return schema.validate(data);
};
module.exports = router;

why do i keep getting 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client' error?

Okay i am fairly new to node js and i am learning user authentication. I keep getting the 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client' error. can someone just tell me what is wrong with my code and how to fix it?. When i test it in postman, The register route works, its the login route that gives me this problem. Here is my code:
const User = require('../models/User')
const CryptoJS = require("crypto-js");
const jwt = require("jsonwebtoken");
const {BadRequestError, UnauthenticatedError} = require('../errors')
const Register = async (req, res)=>{
const newUser = new User({
username: req.body.username,
email: req.body.email,
password: CryptoJS.AES.encrypt(req.body.password, process.env.pass_secret ).toString(),
});
if(newUser){
const savedUser = await newUser.save();
res.status(201).json(savedUser);
}
}
const Login = async (req, res) =>{
const {username} = req.body
//checking if both the email and password are provided
if(!username){
throw new BadRequestError('please provide a username and password')
}
//finding a user with the email, if the user doesnt exist, return an error
const user = await User.findOne({username: req.body.username});
if(!user){
throw new UnauthenticatedError('Invalid username or password')
}
//checking if the passwords match
const hashedPassword = CryptoJS.AES.decrypt( user.password, process.env.pass_secret);
const originalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
if(originalPassword !== req.body.password){
throw new UnauthenticatedError('Invalid email or password')
}
const accessToken = jwt.sign( { id: user._id, isAdmin: user.isAdmin}, process.env.jwt_secret, {expiresIn:"30d"});
const { password, ...others } = user._doc;
res.status(200).json({...others, accessToken});
}
module.exports = {Register, Login}
Wherever you have this:
if(newUser){
const savedUser = await newUser.save();
res.status(201).json(savedUser);
}
You need to change it to:
if(newUser){
const savedUser = await newUser.save();
res.status(201).json(savedUser);
return;
}
You don't want the code to continue after you've done
res.status(201).json(savedUser);
because it will then try to send another response.
In addition, everywhere you have this:
if (err) throw err;
inside an async callback, you need to replace that with something that actually sends an error response such as:
if (err) {
console.log(err);
res.sendStatus(500);
return;
}

How can I get the name of the current user from the Json Web Token?

I am trying to get the name and other info from the jwt and I can so far only get the Id of the user.
This is the routes folder for authentication
const config = require("config");
const jwt = require("jsonwebtoken");
const { User } = require("../models/user");
const mongoose = require("mongoose");
const express = require("express");
const Joi = require("joi");
const _ = require("lodash");
const bcrypt = require("bcrypt");
const router = express.Router();
router.post("/", async (req, res) => {
const { error } = validate(req.body);
if (error) return res.status(400).send(error.details[0].message);
let user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).send("Invalide email or password.");
//compare the passowrd in the body and the password entered when registeration
const validePassword = await bcrypt.compare(req.body.password, user.password);
if (!validePassword)
return res.status(400).send("Invalide email or password.");
const token = user.generateAuthToken();
res.send(token);
});
function validate(req) {
const schema = {
email: Joi.string()
.required()
.min(5)
.max(250)
.email(),
password: Joi.string()
.required()
.min(7)
.max(1024)
};
return Joi.validate(req, schema);
}
module.exports = router;
this is the middleware folder
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function(req, res, next) {
const token = req.header("x-auth-token");
if (!token) return res.status(401).send("Access denied. No token provided.");
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
res.status(400).send("Invalid token.");
}
};
This is the function for getting the current user when they are logged in...
export function getCurrentUser() {
try {
const jwt = localStorage.getItem(tokenKey);
return jwtDecode(jwt);
} catch (ex) {
return null;
}
}
This is all I have in my profile component file where the ComponentDidMount is located...
import React, { Component } from "react";
import { Link } from "react-router-dom";
import NavBar from "./navBar";
import "../profile.css";
import { getCurrentUser } from "../services/authService";
class Profile extends Component {
state = {};
async componentDidMount() {
const currentUser = await getCurrentUser();
console.log(currentUser)
}
When I log the the currentUser this is what I get...
{_id: "5d96a81f9a2f1a8bd485f76c", iat: 1570751361}
iat: 1570751361
_id: "5d96a81f9a2f1a8bd485f76c"
PLEASE HELP
if you need username in payload,you need to add username when you sign the token.If you can't sign with username for some reason,write another service to return username from userId or whatever field exist in your jwt.
userSchema.methods.generateAuthToken = function() {
const token = jwt.sign( { _id: this._id,
isAdmin: this.isAdmin,
_username:this.username },
config.get("jwtPrivateKey") );
return token;
};
i have added new _username:this.username key value pair inside the jwt.sign function, _username is the key and this.username should be your actual username which is in the database.

How to use multiple post functions in node js epxress mongodb

What I'm trying to do here is use the 2 functions but I don't know how to use both of them as they're both post functions with same name and I want to able to test using postman. These are in the same file
const jwt = require('jsonwebtoken');
const Joi = require('joi');
const bcrypt = require('bcrypt');
const _ = require('lodash');
const { users } = require('../models/user');
const express = require('express');
const router = express.Router();
this is the first post function
router.post('/', async (req, res) => {
const { error } = validate(req.body);
if (error) {
return res.status(400).send(error.details[0].message);
}
let user = await users.findOne({ email: req.body.email });
if (!user) {
return res.status(400).send('Incorrect email or password.');
}
const validPassword = await bcrypt.compare(req.body.password,
user.password);
if (!validPassword) {
return res.status(400).send('Incorrect email or password.');
}
const token = jwt.sign({ _id: user._id }, 'PrivateKey');
res.header('x-auth-token', token).send(_.pick(user, ['_id', 'name',
'email']));
});
this is the second post function
router.post('/', async (req, res) => {
const { error } = validate(req.body);
if (error) {
return res.status(400).send(error.details[0].message);
}
let user = await users.findOne({ email: req.body.email });
if (!user) {
return res.status(400).send('Incorrect email or password.');
}
const validPassword = await bcrypt.compare(req.body.password,
user.password);
if (!validPassword) {
return res.status(400).send('Incorrect email or password.');
}
});
function validate(req) {
const schema = {
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(5).max(255).required()
};
return Joi.validate(req, schema);
}
and lastly the last line in the file
module.exports = router;
You cannot have 2 handlers for same REST endpoint with same METHOD. Why don't you change one to router.post('/login')

Resources