Why my data cannot be saved into MongoDB? - node.js

I try to sign up, i submit the form then it logs me undefined, and it doesn't save the user into the database, even the code from the tutorial works perfectly.
Here's my code
exports.postSignup = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const confirmPassword = req.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
return res.redirect('/signup');
}
const user = new User({
email: email,
password: password,
cart: { items: [] }
});
return user.save();
})
.then(result => {
res.redirect('/login');
})
.catch(err => {
console.log(err);
});
};
I have to mention that the user model is correct.

Im not sure, but in my own code I dont save an object within the return line.
Maybe try this
exports.postSignup = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const confirmPassword = req.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
return res.redirect('/signup');
}
const user = new User({
email: email,
password: password,
cart: { items: [] }
});
try{
user.save();
}catch(err){
res.send(err);
}
return user; //only if you want to return a user ofc
})
Other things that maybe going on:
Is your ip whitelisted?
Did you include the connection string?
Do you return a jason object? then make sure to use a parser in your middleware.

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!

add basic auth to express rest api

so I have rest api where i store user data in mongoDB, I want to add basic auth to my api but I'm stuck, I want to check if user is authorised on some paths, for example on /update, if user is auth perfom request, if not send that user is not authorized
my code where I store user is db
const addUser = async (req, res) => {
const checknick = await User.find({ nickname: req.body.nickname }) //checks if user exists with nickname
if (checknick.length !== 0) {
return res.send({
message: 'user already exists, please use another nickname',
})
}
const secretInfo = await hash(req.body.password).catch((err) =>
res.send('password is required!')
)
const user = new User({
name: req.body.name,
surname: req.body.surname,
nickname: req.body.nickname,
password: secretInfo.password,
salt: secretInfo.salt,
})
user.save((err, result) => {
if (err) {
return res.send(err)
}
res.send('user added sucesessfully')
})
}
and where I verify user
const verify = async (req, res) => {
const user = await User.findOne({ nickname: req.body.nickname })
if (!user) {
return
}
const { password } = await hash(req.body.password, user.salt).catch((err) =>
res.send('password is required')
)
const verifiedUser = await User.findOne({
nickname: req.body.nickname,
password: password,
})
if (!verifiedUser) {
return false
}
return true
}
and finally login logic
const login = async (req, res) => {
const access = await verify(req, res)
// console.log(req.headers)
if (access) {
res.send('logged in')
console.log(req.headers)
return
}
return res.status(401).send('failed to login')
}
everything works but I want to use authorizatuon header to send user and password information
This is how to restrict a route add this middleware function before the
route you want to restrict like this:
app.post("/update", restrictTo("admin"));
Every user must have a role to authorize. here I am handling error with a global error handler but you can handle error another way:
exports.restrictTo = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role))
return next(
new AppError('You dont have permission to do this action', 403)
);
next();
};
};

How to display errors with Axios in React

Good day,
i am running to a slight problem with Axios, i want to check if the status code is ok, if not then i send the error to the client. How can i do that ?
This is my attempt to do it, but i had no success:
Route code:
router.post("/register", async (req, res) => {
try {
//check if fields are empty
const { name, email, password, rePassword } = req.body;
if (!name || !email || !password || !rePassword) {
return res.status(406).send("fields are empty");
}
//passsword length should be at leats 6 characters
if (password.length < 6) {
return res
.status(406)
.send("Password should at least be 6 characters long");
}
//checking if passwords match
if (password !== rePassword) {
return res.status(406).send("Passwords dont match");
}
//check if user exists
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) {
return res.status(406).send("User already exists");
} else {
//create salt
const salt = await genSalt(10);
//hashing the password
const hashedPassword = await hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
isAdmin: req.body.isAdmin,
});
const newUser = await user.save();
res.status(200).send(newUser);
}
} catch (err) {
console.log(err);
}
});
Page code:
const handleSubmit = async e => {
e.preventDefault();
axios
.post("http://localhost:5000/users/register", {
name,
email,
password,
rePassword,
})
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}
The response will give you the following options
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
This will allow you to check for valid stausText/codes, or display errors accordingly.

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.

Using bcrypt on Node.js results in validation failed

I am writing code in Node.js to encrypt passwords using bcrypt.
However, if you use bcrypt, you will get an ValidationError: User validation failed: password: Cast to String failed for value "Promise { <pending> }" at path "password"
I do not get this error if I save it as plain text without encryption.
Is there a secret of bcrypt I do not know?
bcrypt (not working)
const bcrypt = require('bcrypt');
sign_up = (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
const hash = bcrypt.hash(password, 8);
const user = new User({
email: email,
password: hash
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
no bcrypt (working)
sign_up = (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
const user = new User({
email: email,
password: password
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
To elaborate on Chris's comment:
It appears that bcrypt.hash is asynchronous, and is returning a Promise.
To fix this, I would recommend using an async function and awaiting the result. MDN page
This may require a newer version of NodeJS than what you are running.
const bcrypt = require('bcrypt');
// Async function allows us to use await
sign_up = async (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
// We await the result of the hash function
const hash = await bcrypt.hash(password, 8);
const user = new User({
email: email,
password: hash
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
Do not use the bcrypt.hashSync function, as while it is running your server will not be able to do anything else.

Resources