Invalid email or password - node.js

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);
}
});

Related

Reactjs: post data to localhost always pending

I am working on ReactJs and NodeJS and I am creating a signup page. I post data to server but it is always pending.
Which part did I do wrong? It would be nice if someone can help.
Front end:
const handleSubmit = (event) => {
// prevent page refresh
event.preventDefault();
const newUserData = {
name: name,
email: email,
password: password,
};
axios
.post("/signup", newUserData)
.then((res) => {
console.log(res.data);
})
.catch((error) => {
console.log(error);
});
setEmail("");
setName("");
setPassword("")
console.log("form submitted ✅");
};
Backend:
router.post("/signup", (req, res) => {
const { name, email, password } = req.body;
if (!email || !password || !name) {
res.status(422).send({ error: "Please add all the fields" });
}
console.log(req.body);
User.findOne({ email: email })
.then((savedUser) => {
if (savedUser) {
res.status(422).send({ error: "Email already been used" });
}
bcrypt.hash(password, 12).then((hashedpassword) => {
const user = new User({
name,
email,
password: hashedpassword,
});
user
.save()
.then((user) => {
res.json({ message: "Sign Up Successfully" });
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
});
in package.json i set proxy as
"proxy": "http://localhost:5000",
I guess you are using MongoDB as well, in that case keep in your mind that the findOne is async, so you need to use await before. And for to save data you need to use the .create() method from MongoDB, e.g.
router.post("/signup", async (req, res) => {
const { name, email, password } = req.body;
if (!email || !password || !name) {
res.status(422).send({ error: "Please add all the fields" });
}
console.log(req.body);
await User.findOne({ email: email })
.then((savedUser) => {
if (savedUser) {
// you need to add return to stop the code
return res.status(422).send({ error: "Email already been used" });
}
// or you can add else because the code keep running
bcrypt.hash(password, 12).then((hashedpassword) => {
const user = await User.create({
name,
email,
password: hashedpassword,
});
user
.save()
.then((user) => {
res.json({ message: "Sign Up Successfully" });
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
});
I think it is better to use something like throw new Error('Email already been used') instead of return for your res.status(422).send({ error: "Email already been used" }); because if you have return the server doesn't give back an error, but a normal answer, but of course it is ok if you want that.
I want you to be sure that before you submit, the values name, email, password, are updated. Please try:
const handleSubmit = async (event) => {
// prevent page refresh
event.preventDefault();
console.log(`The value for the name: ${name}`);
console.log(`The value for the email: ${email}`);
console.log(`The value for the password: ${password}`);
try {
const response = await axios.post("http://localhost:5000/signup", {
name,
email,
password,
});
console.log(response.data);
setEmail("");
setName("");
setPassword("");
console.log("form submitted ✅");
} catch (error) {
console.log(error);
}
};

When I compare my password through Insomnia API tester by doing tests on username and password for the first time it works but then server crashes

router.post("/auth", async (req, res) => {
const { username, password } = req.body;
const user = await login.findOne({ where: { username: username } });
if (!user) res.json({ error: "User Doesn't Exist" });
bcrypt.compare(password, user.password).then((match) => {
if (!match) res.json({ error: "Wrong Username And Password Combination" });
res.json("YOU LOGGED IN!!!");
});
})
module.exports = router;enter image description here
This error was due to not catching errors properly the correct way of catching errors will be like:
router.post("/auth", async (req, res) => {
const { username, password } = req.body;
const user = await login.findOne({
where: { username: username }
});
if (!user)
res.json({ error: "User Doesn't Exist" })
else {
bcrypt
.compare(password, user.password)
.then((match) => {
if (!match)
res.json({ error: "Wrong Username And Password Combination" })
else {
res.json("Logged in");
}
});
}
})
module.exports = router;

How can I create secure CRUD operation using MERN stack

I'm new to MERN stack and I did CRUD operation. I want to hash the password upon creation of new user, since I'm getting an error after creating a new user and trying to login "Invalid credentials" since the new user has been created with the password plain text and my registration compares the password with the hashed one
My create new user code:
exports.create = (req, res) => {
if(!req.body.name || !req.body.email || !req.body.password) {
return res.status(400).send({
message: "Name, Email and Password can not be empty"
});
}
const user = new User({
name: req.body.name.trim(),
email: req.body.email.trim(),
password: req.body.password.trim()
});
user.save()
.then(data => {
const user = usersSerializer(data)
res.send(user);
}).catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while creating the User."
});
});
};
You must hash the user's password before storing it in the database. Do something like this.
router.post(
'/register',
[
check('email', 'Uncorrectly e-mail').isEmail(),
check('password', 'Uncorrectly password').isLength({ min: 6 })
],
async (req, res) => {
try {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json ({
errors: errors.array(),
message: 'Incorrect registration data'
})
}
console.log(req.body)
const { email, password, firstName, lastName } = req.body
const candidate = await User.findOne({ email })
if (candidate) {
return res.status(400).json({ message: 'User already exist' })
}
const hashedPassword = await bcrypt.hash(password,12)
const user = new User ({email, password: hashedPassword, firstName, lastName})
await user.save()
res.status(201).json({ message: 'New user created' })
} catch (error) {
res.status(500).json ({ message: 'ERROR' })
}
})

Solved: my code is unable to encrypt the password to save in mongoDB after email activation token is decrypted in node js, how can i solve it?

i am trying to implement jwt authentication by MERN and in nodejs i have used an email activation link to save the user email and password in mongodb. here is my working example for registering the user and activating the user. i am using sendgrid for the email.
//for email verification test
signup = async (req, res) => {
const { username, email, password, passwordCheck, displayName } = req.body;
//validate
if (!username || !email || !password || !passwordCheck)
return res.status(400).json({
msg: "not all fields have been entered.",
});
if (password.length < 8)
return res.status(400).json({
msg: "The password needs to be at least 8 characters long.",
});
if (password !== passwordCheck)
return res
.status(400)
.json({ msg: "Enter the same password twice for verification." });
const existingUser = await User.findOne({ email: email });
if (existingUser)
return res
.status(400)
.json({ msg: "An account with this email already exists." });
const existingUserName = await User.findOne({ username: username });
if (existingUserName)
return res
.status(400)
.json({ msg: "An account with this username already exists." });
if (!displayName) displayName = email;
const token = jwt.sign(
{ username, email, password, passwordCheck, displayName },
process.env.JWT_SECRET,{expiresIn:'1000m'}
);
const msg = {
to: email, //receiver's email
from: "no-reply#test.com", // Change to your verified sender
subject: `Email verification link ${displayName}`,
text: "testing from local",
html: `<h2>Hi ${displayName}</h2> <br/>sends a message for verification test: http://localhost:3000/authentication/activate/${token}</p> <br/><p>Have a nice day</p>`,
};
sgMail.setApiKey(process.env.SENDGRID_SECRET_API);
sgMail
.send(msg)
.then((result) => {
res.json({ message: "Email activation link has been sent" });
})
.catch((error) => {
console.error(error);
res.status(500).json("Error");
});
}
router.post("/register", signup);
userActivation = (req, res)=>{
const { token } = req.body;
if(token){
jwt.verify(token, process.env.JWT_SECRET,function(err,decodeToken){
if(err){
return res.status(400).json({error:'Incorrect or expired link.'})
}
const { username, email, password, passwordCheck, displayName }=decodeToken;
const newUser = new User({
username,
email,
password,
displayName,
});
newUser.save((err,success)=>{
if(err){
console.log("Error in signup with account activation",err)
return res.status(400).json({error:"Error activating account"})
}
res.json({
message:"signup Success!!"
})
});
} );
} else{
return res.json({error:"Something went wrong"})
}
}
router.post("/email-activate",userActivation)
while using the postman to save the user with activation key, in mongodb the password is saving in plain text. i don want it to be saved in plain text because of security issue. i want it to be saved in encrypted and tried to use the below code:
userActivation = async (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, process.env.JWT_SECRET, function (err, decodeToken) {
if (err) {
return res.status(400).json({ error: "Incorrect or expired link." });
}
const { username, email, password, displayName } = decodeToken;
console.log(password)
User.findOne({ email }).exec((err, user) => {
if (user) {
return res.status(400).json({ error: "Username with this email exists." })
}
const salt = bcrypt.genSalt();
bcrypt.hash(password, salt, (err, passwordHash)=>{
const newUser = new User({
username,
email,
password: passwordHash,
displayName,
});
console.log(password)
console.log(passwordHash)
newUser.save((err, success) => {
if (err) {
console.log("Error in signup with account activation", err);
return res.status(400).json({ error: "Error activating account" });
}
res.json({
message: "signup Success!!",
});
})
})
})
})
}
}
when i start my server and try to sign in with activation key through postman it sends me an activation link. when i try to send the post request through the postman in activation link,postman shows 404 status with "error activating account" and node index shows the following error:
The server has started on port: 5000
MongoDB connected
**the real password is showing undecoded**
**the real password is showing undecoded**
undefined
Error in signup with account activation Error: user validation failed: password: Path `password` is required.
at ValidationError.inspect (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\error\validation.js:47:26)
at formatValue (internal/util/inspect.js:731:31)
at inspect (internal/util/inspect.js:295:10)
at formatWithOptionsInternal (internal/util/inspect.js:1958:40)
at formatWithOptions (internal/util/inspect.js:1842:10)
at Object.value (internal/console/constructor.js:306:14)
at Object.log (internal/console/constructor.js:341:61)
at C:\Myfiles\Reactjs\Projects\test-projects\routes\userRouter.js:243:37
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\model.js:4863:16
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\helpers\promiseOrCallback.js:16:11
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\model.js:4886:21
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\model.js:500:16
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\kareem\index.js:247:48
at next (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\kareem\index.js:168:27)
at next (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\kareem\index.js:170:9)
at Kareem.execPost (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\kareem\index.js:218:3) {
errors: {
password: ValidatorError: Path `password` is required.
at validate (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\schematype.js:1256:13)
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\schematype.js:1239:7
at Array.forEach (<anonymous>)
at SchemaString.SchemaType.doValidate (C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\schematype.js:1184:14)
at C:\Myfiles\Reactjs\Projects\test-projects\node_modules\mongoose\lib\document.js:2502:18
at processTicksAndRejections (internal/process/task_queues.js:75:11) {
properties: [Object],
kind: 'required',
path: 'password',
value: undefined,
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: 'user validation failed'
}
According to above try the code is unable to encrypt the password and hence it can't be saved in mongodb. what did i do wrong to encode the password?
so how can i solve it?
thanx in advance
const passwordHash = bcrypt.hash(password, salt);
Here bcrypt.hash returning promise either your can use async/await or use .then().
userActivation = async (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, process.env.JWT_SECRET, function (err, decodeToken) {
if (err) {
return res.status(400).json({ error: "Incorrect or expired link." });
}
const { username, email, password, displayName } = decodeToken;
console.log(password);
User.findOne({ email }).exec((err, user) => {
if (user) {
return res.status(400).json({ error: "Username with this email exists." })
}
//Use genSaltSync when you donot want to use await or your can use await bcrypt.genSalt()
const salt = bcrypt.genSaltSync(10);
bcrypt.hash(password, salt, (err, passwordHash)=>{
const newUser = new User({
username,
email,
password: passwordHash,
displayName,
});
newUser.save((err, success) => {
if (err) {
console.log("Error in signup with account activation", err);
return res.status(400).json({ error: "Error activating account" });
}
res.json({
message: "signup Success!!",
});
})
})
})
})
}
}
Try this code once just put await before bcrypt and made function async.

Getting error while logging in 402 (Payment Required)

I don't understand it. I am not able to login. User is already in my database, and when I log in, it simply says:
POST http://localhost:3000/api/v1/users/login 402 (Payment Required)
When I register for the first time, and then login, login is successful. If I logout, and then try to log in with that same email and password, it's throwing me the above error. I'm not even using someone's API. It's my own created one. It's sending me a response of "incorrect password"
Here's the controller:
loginUser: (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return res.status(400).json({ message: "Email and password are must" })
}
User.findOne({ email }, (err, user) => {
if (err) {
return next(err)
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invalid email" })
} else if (!user) {
return res.status(402).json({ error: "User not found" })
} else if (!user.confirmPassword(password)) {
return res.status(402).json({ error: "incorrect password" })
}
})
}
User model
const mongoose = require("mongoose")
const bcrypt = require("bcrypt")
const Schema = mongoose.Schema
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, reuired: true },
password: { type: String, required: true },
posts:[{ type: Schema.Types.ObjectId, ref: "Post" }]
}, { timestamps: true })
userSchema.pre("save", function (next) {
if (this.password) {
const salt = bcrypt.genSaltSync(10)
this.password = bcrypt.hashSync(this.password, salt)
}
next()
})
userSchema.methods.confirmPassword = function (password) {
return bcrypt.compareSync(password, this.password)
}
const User = mongoose.model("User", userSchema)
module.exports = User
registration controller
registerUser: (req, res) => {
const { username, email, password } = req.body
User.create(req.body, (err, createdUser) => {
if (err) {
return res.status(500).json({ error: "Server error occurred" })
} else if (!username || !email || !password) {
return res.status(400).json({ message: "Username, email and password are must" })
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invaid email" })
} else if (password.length < 6) {
return res.status(400).json({ message: "Password should be of at least 6 characters" })
}
else {
return res.status(200).json({ user: createdUser })
}
})
}
Edit
loginUser: async (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return res.status(400).json({ message: "Email and password are must" })
}
await User.findOne({ email }, (err, user) => {
if (err) {
return next(err)
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invalid email" })
} else if (!user) {
return res.status(402).json({ error: "User not found" })
} else if (!user.confirmPassword(password)) {
return res.status(402).json({ error: "incorrect password" })
}
})
}
new post controller
newPost: (req, res) => {
const data = {
title: req.body.title,
content: req.body.content,
user: req.user.userId
}
Post.create(data, (err, newPost) => {
if (err) {
return res.status(500).json({ error: err })
} else if (!newPost) {
return res.status(400).json({ message: "No Post found" })
} else if (newPost) {
User.findById(req.user.userId, (err, user) => {
user.posts.push(newPost._id) //pushing posts documnet objectid to the post array of the user document
user
.save()
.then(() => {
return res.json(200).json({ user })
})
.catch(err => {
return res.status(500).json({ error: err })
})
})
}
})
}
You might want to refactor your code so that you do the bcrypt operations in controller not in the model. You are checking this.password after the user is updated (creating new posts) and since this is the user, the below code is being met each time you update the user object.
if (this.password) {
const salt = bcrypt.genSaltSync(10)
this.password = bcrypt.hashSync(this.password, salt)
}
So your hashing it every time you update the user (create a post). Instead, remove the above code from the userSchema.pre(...) and try doing the bcrypt hashing only when the user first registers.
registerUser: (req, res) => {
var { username, email, password } = req.body
if (password) {
const salt = bcrypt.genSaltSync(10)
password = bcrypt.hashSync(password, salt)
}
User.create(req.body, (err, createdUser) => {
if (err) {
return res.status(500).json({ error: "Server error occurred" })
} else if (!username || !email || !password) {
return res.status(400).json({ message: "Username, email and password are must" })
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invaid email" })
} else if (password.length < 6) {
return res.status(400).json({ message: "Password should be of at least 6 characters" })
}
else {
return res.status(200).json({ user: createdUser })
}
})
}
This way the hashing occurs only once at the creation of the user and should remain consistent throughout other operations.
As for the Can't set headers after they are sent error, you might be sending a response twice, since the error appears to come from the posts controller. You are likely sending the user response and the post response. Maybe don't send the posts response since you will be sending it along in the user response.
More info on the error here.

Resources