How to save the Reset Password Nodejs and MongoDb - node.js

I am using SendGrid to send the user the reset password link that goes with two parameters (The user._id and token). I have another component that saves the user's changed the password but all I get is an error user. save is not a function
Email helper Code.
import sendGrid from "#sendgrid/mail";
export class sendGridEmail {
static async sendResetPasswordEmail(email, token, id) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `Follow this link to reset your password: ${process.env.BASE_URL}/${id}/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
sendLink Component
export const resetUserPassword = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "the email provided was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token, user._id);
res.status(200);
res.json({
message: `a link to reset your password has been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Component that tries to update the password in the Database but I get an error user.save() is not a function
export const saveResetPassword = asynchandler(async (req, res) => {
const { id, authorization } = req.params;
const user = userModel.findOne(req.params.id);
const private_key=process.env.PRIVATE_KEY
const payload = jwt.verify(authorization, private_key);
if (user._id === id || payload.id) {
try {
user.password = req.body.password;
await user.save();
} catch (error) {
res.status(404);
res.json({ message: `an error occured: ${error}` });
}
}else{
res.status(500)
res.json({message: "an error occured"})
}
});
My Routes
import { loginUser, registerUser, resetUserPassword, saveResetPassword } from "./controllers/user.controller.js";
export const Routes =(app)=>{
app.get("/health", (req,res) => {
res.send(200).json({message:"Server health check is Ok"});
});
// user api's
app.post('/api/registeruser', registerUser);
app.post('/api/loginuser', loginUser);
app.post('/api/password-reset', resetUserPassword);
app.post("/api/save-password/:id/:authorization", saveResetPassword);
}

const user = await userModel.findOne(req.params.id);
You forgot await, model.findOne() returns a Promise

Related

SyntaxError: await is only valid in async functions and the top level bodies of modules - NodeJS

I am trying to create user login and sign up in NodeJS with mongoDB, but in login module i am getting this error -
existingUser = await User.findOne({email: email});
^^^^^
SyntaxError: await is only valid in async functions and the top level
bodies of modules.
Here is my code of "user-controller.js" file code.
const User = require('../model/User');
const bcrypt = require('bcryptjs');
// next is used to move to the next middleware task
const signup = async (req, res, next) => {
const { name, email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
console.log(err);
}
if (existingUser) {
return res.status(400).jason({ message: 'User already exists! Login Instead' })
}
const hashedPassword = bcrypt.hashSync(password);
const user = new User({
name,
email,
password: hashedPassword,
});
try {
await user.save();
} catch (err) {
console.log(err);
}
return res.status(201).json({ message: user })
};
const login = (req, res, next) => {
const { email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
return new Error(err);
}
if (!existingUser) {
return res.status(400).json({ message: "User not found. Signup Please" })
}
const isPasswordCorrect = bcrypt.compareSync(password, existingUser.password);
if (!isPasswordCorrect) {
return res.status(400).json({ message: "Invalid Email / Password" })
}
return res.status(200).json({ message: "Successfully logged in" })
}
exports.signup = signup;
exports.login = login;
How to resolve it?
We can only use await inside an async function, in your case
const login = async (req, res, next) => {
// We can use await here
}
Instead of try catch we can do something like this
try {
User.findOne({ email: email }).then((response)=>{
//do something
});
} catch (err) {
//do something
}

Getting an undefined token in backend

Hello i am trying to use my token in my application after user is logged in but am getting an undefined response in my console. Below are my codes. How can i correct my code to be able to access token inside application and use to do other features of the application?
my controller
import User from "../models/user";
import Stripe from "stripe";
const stripe = Stripe(process.env.STRIPE_SECRET);
export const createConnectAccount = async (req, res) => {
console.log(req.user);
try {
const user = await User.findById(req.user._id).exec();
console.log("USER ==> ", user);
if (!user.stripe_account_id) {
const account = await stripe.accounts.create({
type: "express",
});
console.log("ACCOUNT ===>", account);
user.stripe_account_id = account.id;
user.save();
}
} catch (error) {
res.status(500).json();
}
};
my middleware
var { expressjwt: jwt } = require("express-jwt");
// req.user
export const requireSignin = jwt({
//secret, expiryDate
secret: process.env.JWT_SECRET,
algorithms: ["HS256"],
});
my routes
import express from "express";
const router = express.Router();
import { requireSignin } from "../middlewares";
import { createConnectAccount } from "../controllers/stripe";
router.post("/create-connect-account", requireSignin, createConnectAccount);
module.exports = router;
my auth controller
import User from "../models/user";
import jwt from "jsonwebtoken";
export const register = async (req, res) => {
console.log(req.body);
const { name, email, password } = req.body;
if (!name) return res.status(400).send("Name is required");
if (!password || password.length < 6)
return res
.status(400)
.send("Password is required and should be minimum 6 characters long");
let userExist = await User.findOne({ email }).exec();
if (userExist) return res.status(400).send("Email is taken");
const user = new User(req.body);
try {
await user.save();
console.log("User saved successfully", user);
return res.json({ ok: true });
} catch (err) {
console.log("CREATE USER FAILED", err);
return res.status(400).send("Error.Try again");
}
};
export const login = async (req, res) => {
// console.log(req.body);
const { email, password } = req.body;
try {
//check if user with credentials
let user = await User.findOne({ email }).exec();
// console.log("USER EXISTS", user);
if (!user) res.status(400).send("User with email not found");
//compare password
user.comparePassword(password, (err, match) => {
console.log("COMPARE PASSWORD IN LOGIN ERR", err);
if (!match || err) return res.status(400).send("Wrong password");
//("GENERATE A TOKEN THEN SEND AS RESPONSE TO CLIENT");
let token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, {
expiresIn: "7d",
});
res.json({
token,
user: {
_id: user._id,
name: user.name,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
},
});
});
} catch (err) {
console.log("LOGIN ERROR", err);
res.status(400).send("Signin failed");
}
};
my terminal output
POST /api/login 200 1142.309 ms - 349
undefined
POST /api/create-connect-account 500 9.092 ms - -
Headers
import axios from "axios";
export const createConnectAccount = async (token) => {
await axios.post(
`${process.env.REACT_APP_API}/create-connect-account`,
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
};
I'm sorry to tell you your code has other errors in it.
My guess is that your res is not well written in auth controller, login function :
res.status(201).json({
token :token,
user: user
})
Also when reading your token trying to authenticate : it will be easier to use the same package than the one that sign it.
const jwt = require("jsonwebtoken");
exports. requireSignin = () => {
return async (req, res, next) => {
try {
const token = req?.headers?.authorization?.split(" ")[1];
const decodedToken = jwt.verify(token, process.env.JWT_SECRET);
const userId = decodedToken._id;
const user = await User.findOne({ _id: userId });
if (user) {
req.auth = {
user: user,
};
} else {
throw new Error("user not found");
}
next();
} catch (error) {
console.log(error.message);
res.status(401).json({ error: "failed to authenticate" });
}
};
};
But your code is pretty hard to read :
To make it easier to read and clearer for you, try and use joy or yup
Joi : https://www.npmjs.com/package/joi
Yup : https://www.npmjs.com/package/yup
With those you will be able to create middlewares to avoid wrong entries in your body : for example
if (!name) return res.status(400).send("Name is required");
is processed automatically with those packages
Also, you shouldn't use 'import' and 'require' in the same project, choose either one of them
I hope this will help

I am trying to save the reset password back to database but request taking longer until i cancel

I am not receiving any errors but when I test my endpoint it's taking longer and no response until I terminate the request. I am sending a user an email with a token that will be used to validate if he exists in the database so that he can change his/her password but I have not succeeded for the last two days. I am frustrated now, I have never done this before.
The Middleware that sends the reset password link
export class sendGridEmail {
static async sendResetPasswordEmail(email, token) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `You are receiving this email because you (or someone else) has requested the reset of a password. Follow this link ${process.env.BASE_URL}/api/resetpassword/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
}
The Component that sends the reset password link
export const sendResetPasswordLink = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "account with this email was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token);
res.status(200);
res.json({
message: `password reset link hase been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Route that tries to save the password. Am I getting it wrong by verifying the token in the params using jwt and then checking if the user exists or am I missing out something ?
export const resetPassword = asynchandler(async (req, res) => {
const { resetToken } = req.params;
const private_key = process.env.PRIVATE_KEY;
const payload = jwt.verify(resetToken, private_key);
const user = await userModel.findById(payload.id);
console.log(payload.id);
if (!user) {
res.status(404);
res.jsonp({ message: "token has expired" });
}else if(user){
user.password= req.body.password
await user.save();
await resetToken.delete();
await sendMessage.sendPasswordResetSuccess(user.number);
res.status(200);
res.json({message:"password changed succesfully"});
}else{
res.status(500)
res.json({message:"no token was procide"})
}
});
The routes
app.post('/api/resetlink', sendResetPasswordLink);
app.put("/api/resetpassword/:resetToken", resetPassword);

can't set status of 400 in express

I have a simple web service and it has a route for register user ,
I want when email exists in DB throw an error with status of 400 or other
I've done it like this
controllers/user.js
const { User } = require('../models/user')
exports.create = async (req, res) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
return res.json({ err: 'email already exists' })
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
res.status(400).send({ err })
}
}
BUT , it always give status of 200,
where is the problem ?
Add the status to your response:
if (user) {
return res.status(400).json({ err: 'email already exists' })
}
You can simply send the status 400 when checking if(user)
if(user){
res.status(400).jsom({ err: "Email already exists" });
}
OR
Threat the errors and add a middleware using next (a little bit more complicated then the first one, but more proffessional)
exports.create = async (req, res, next) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
throw new Error("Email already exists");
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
next(err, req, res, next);
}
}
In the next middleware you can threat the error and send whatever response you need. (err, req, res objects are sent like references, so you can use them there)

Invalid email or password

don't know whats going on wrong,when i am trying to post request through postman i am getting an error like "Invalid email or password". in sign in. please help
signup
below is my signup request where i am doing my signup validation.
const User = require('../model/user');
const bcrypt = require('bcryptjs');
exports.signup = (req, res) => {
const { name, email, password } = req.body;
if (!name || !email || !password) {
res.status(422).json({
error: "please add all field"
})
}
User.findOne({ email: email })
.then((SavedUser) => {
if (SavedUser) {
return res.status(400).json({
error: "User already exists that email"
})
}
const user = new User({
email,
password,
name
})
user.save()
.then(user => {
res.json({
message: "saved Successfully"
})
.catch(err => {
console.log(err);
})
})
.catch(err => {
console.log(err);
})
})
}
Signin
below is my signin form where i doing my signin operation
exports.signin = (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
res.status(422).json({
error: "please enter email and password"
})
}
User.findOne({ email: email })
.then(SavedUser => {
if (!SavedUser) {
return res.status(400).json({
error: "invalid email or password"
})
}
bcrypt.compare(password, SavedUser.password)
.then(doMatch => {
if (doMatch) {
res.json({
message: "Successfully Signed in"
})
}
else {
return res.status(422).json({
error: "Invalid email or password"
})
}
})
.catch(err => {
console.log(err);
})
})
}
It seems you're not hasing the password, when creating a new mongoose user-object. Obvioulsy, bcrypt.compare(password, SavedUser.password) will then fail. Try to do it like this (note I'm using async/await here instead of promises directly):
password = await bcrypt.hash(password, 10);
const user = new User({
email,
password,
name
});
you didn't bcrypt your password at the time of saving.
You can make a pre save function in your schema like this.
// Hash the plain text password before saving
User.pre("save", async function (next) {
const user = this;
try {
if (user.isModified("password")) {
user.password = await bcrypt.hash(user.password, 8);
}
next();
} catch (error) {
next(error);
}
});

Resources