User.findOne returns null - node.js

router.post("/login", async (req, res) =>
{
try
{
console.log(req.body.email)
const user = await User.findOne({email: req.body.email, password: req.body.password})
if(user)
{
return res.redirect("/")
}
console.log(user)
res.status(200).send(user)
}
catch(e)
{
res.status(400).send(e)
}
})
I am trying to look for a user in MongoDB but the user variable returns null and I am getting status code 200 with an empty object.
Thanks for answers. I found the missing point. I stored the hashed password in database but I forget to search for hashed password

First of all, you should consider not storing passwords as plaintext in your database and when finding a user querying them. I recommend using a hashing algorithm like bcrypt to validate them.
Regarding your question, you have to know that mongoose does not throw an error when it finds no entries and you only send 404 when an error is thrown. I would recommend you changing your code to the following:
router.post("/login", async (req, res) =>
{
try
{
console.log(req.body.email)
const user = await User.findOne({email: req.body.email})
if (user) {
// TODO: Use hashing algorithm to verify password
console.log(user)
res.status(200).send(user)
return res.redirect("/")
} else {
res.status(400).send("User not found")
}
}
catch(e)
{
res.status(500).send("Server error: " + e.message)
}
})

This didn't match the query. Are you sure the e-mail and password are correct? The syntax is correct. It shows status code of 200, that means your code is perfect, but your email pr password is wrong or there is no data in the database with that email and password. Like #Adwirawien said you should hash your password.

looks like your condition:
if(user)
should be written
if(!user)
Or am I wrong ?

Related

Passport fails authentication with "Wrong password" before actually calling my passwordMatch function

This is a weird one.
What im trying to do
Create an authentication server in node.js using Passportjs local strategy, and JWT. Allowing for account registration with email & password, with passwords hashed with 'crypto'
What's happening
So when I login with the right password, to a pre existing model, authentication fails in the APi for having a wrong password. Though theres some weird stuff going on.
What I've tried
Essentially when I make the post request:
OPTIONS /api/login calls
It goes through my passport config, and in the typical function where you check if the password is correct
side note: POST api/login is logged to the console
The function in my passport config:
if (!profileController.passMatch(username, password)) {
console.log('pass was wrong');
return done(null, false, {
message: 'Password is wrong'
});
}
The 'pass was wrong' thing calls, failing the authentication with done(). Though in passMatch, as you'll see below, it does show the correct password
passMatch function in profile controller:
module.exports.passMatch = (email, password) => {
User.findOne({email: email}, (err, user) => {
if (err) { console.log ("error at passMatch: " + err); }
var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
console.log(user.hash == hash);
return (user.hash == hash);
});
return false;
};
Though if you notice the console log where I check if the hash comparison is correct. That log statement is printed to the console after 'pass was wrong' is logged. It's also printed after the passport.authenticate call in my login function concludes a failed authentication at the console.log(info)
Login function:
module.exports.login = (req, res) => {
console.log('beginning to authenticate');
passport.authenticate('local', (err, user, info) => {
console.log ("authenticating");
var token;
// If passport throws an error
if (err) {
res.status(404).json(err);
console.log("error logging in");
return;
}
// If a user is found
if (user) {
// Respond with JWT
token = createJwt(user)
res.status(200);
res.json({
"token": token
})
console.log("user logged in");
// If a user wasn't found
} else {
res.status(401).json(info);
console.log(info);
}
})(req, res);
};
Error logging in isn't called, but the console.log(info) is called with the error message from the done() message in the config.
What's going wrong here?
In the "passMatch" function, I query for the user again (which is just inefficient), but since this operation was asynch, it was being skipped to the "return false" statement after, and in the passport authentication config process, it recieved that false, causing authentication to fail, but the "log" to be returned after cause it took longer.
How I fixed it
I passed in the user object that passport already queried instead of the username into passMatch, then had two operations to check if the hash was the same and returned that, and now it works.
The new code
module.exports.passMatch = (user, password) => {
var hash = crypto.pbkdf2Sync(password, user.salt, 1000, 64, 'sha512').toString('hex');
return user.hash == hash;
};
Also the necessary change in the passport config to pass in the user instead of the username as the first param to that function.

how to compare password in passport-local-mongoose

I would like to compare a logged in user's password, with the password they provided from a form, because i need to make sure that it is really the user, cause i wish to provide an option for them to enable 2fa, and i believe anyone can enable that if the user is logged into the dashboard. Am using bcrypt to compare the password but it throws error.. also am using passport-local-mongoose for registering my users and for authentication.
router.post("/enableTwoFa/:id", isLoggedIn, isVerified, function(req, res){
if(req.isAuthenticated){
User.findById(req.params.id, function(err,found){
if(err){
res.redirect("back")
res.send('User not found with the proper ID')
} else {
if(req.body.accountPassword){
console.log(found)
bcrypt.compare(req.body.accountPassword,found.password, function(err,success){
if(err){
console.log(err)
res.send('There was a problem somewhere')
} else {
console.log(success)
res.send('password hashed')
}
})
} else {
res.send('Please input your account password!!')
}
}
})
} else {
res.send('You are not authorized for this request')
}
})
it throws up an error of missing data and hash. But i don't know how to find the required field.. or if there is a function that handles this in passport, am new to this.
Error: data and hash arguments required
at Object.compare (/home/ubuntu/workspace/bit-main/main/node_modules/bcrypt/bcrypt.js:209:17)
at /home/ubuntu/workspace/bit-main/main/routes/dashboard.js:448:24
at Query.<anonymous> (/home/ubuntu/workspace/bit-main/main/node_modules/mongoose/lib/model.js:3928:16)
at /home/ubuntu/workspace/bit-main/main/node_modules/kareem/index.js:297:21
at /home/ubuntu/workspace/bit-main/main/node_modules/kareem/index.js:135:16
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
After doing some research on this question, i found out that there is an authenticate() function in passport-local-mongoose that handles this request so no need of using bcrypt.
and it accepts 3 cb(hashError,model,passwordError). So i decided to answer it incase someone encounter same problem.
router.post("/enable2fa/:id", isVerified, function(req, res){
if(req.isAuthenticated){
User.findById(req.params.id, function(err,found){
if(err){
res.redirect("back")
res.send('User not found with the proper ID')
} else {
if(req.body.accountPassword){
found.authenticate(req.body.accountPassword, function(err,model,passwordError){
if(passwordError){
console.log(err)
res.send('The given password is incorrect!!')
} else if(model) {
console.log(`correct password ${model}`)
*run other code stuff*
res.send('2fa enabled for this account')
}
})
} else {
res.send('Please input your account password!!')
}
}
})
} else {
res.send('You are not authorized for this request')
}
})

passport-local-mongoose changePassword function

I want to have the functionality that user can change their password.
I've implemented a route ('/resetPasswd') like this:
UserRouter.route('/resetPasswd')
.post(function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
user.changePassword(req.body.oldPassword, req.body.newPassword, function (err, user) {
if (err) next(err);
res.json('password changes successfully !');
})
})(req, res, next);
});
this is what I send as the body:
{
"oldpassword": "secret",
"newPassword": "new"
}
But I get this error as response:
{
"message": "user.changePassword is not a function",
"error": {}
}
and this is a picture of my schema:
user schema:
I don't think I should declare the changePassword function in my schema (since it is provided by the passport-local-mongoose, although I added it but still get the same error) What mistake am I making here?
Someone had the same issue last night actually. Their problem was the package needed to be updated. I would check that you're on the latest version.
Here is what i did in my controller handling the reset password,
exports.editPassword = async (req, res) => {
const user = await User.findOne({
username: req.user.username
});
await user.setPassword(req.body.password);
const updatedUser = await user.save();
req.login(updatedUser);
req.flash('success', 'Password Changed Successfully') res.redirect('back')
}
from the Documentation Passport-local-mongoose ,you first need to get the specific user to update the password , here in my case the current login user , which is available on the req.user which we are exposed to , you can use any of the return property to query your collection, using async await i made a variable to hold the return object, in my case 'user', thereafter i chained the setProperty on it passing in the new password(req.body.password) since it return a promise i await it and assign a variable to it. from here you are good ...Note: since it is a promise it either resolved of reject, handling error can be done by rapping your code in a safe blanket, try..catch . You can read more Here
Since changePassword is a schema method, it must be used on an instance of a model, not the model itself or the imported passportLocalMongoose.
UserModel.findById(req.user._id)
// I assume you already have authentication and the req.user is generated
.then(foundUser => {
foundUser.changePassword(req.body.old, req.body.new)
.then(() => {
console.log('password changed');
})
.catch((error) => {
console.log(error);
})
})
.catch((error) => {
console.log(error);
});
the user object passport sends in callback function is just an object and not a schema instance document object, thus it does not have the changePassword function.

Simple Auth middleware with bcrypt is not functionning on postman

I am quite new in backend, and its my first time using a middleware with nodejs. Until now, my register function and format password are functionning on postman, since when I register a new user, I find on my DB a hashed password.
however, when I want to log in with the same user, I receive my message (wrong password or mail).
my console.log(user) read properly the user profil on node, but after, it seems like bcrypt.compareSync is not functionning. id my console.log(token), returns nothing.
I surely have a problem on my generateToken function or on my login function but I cannot figure it out.
you can see below my code.
login (req,res){
Users.find({mail:req.body.mail})
.then(users =>{
console.log(users);
if(users.length > 0 && bcrypt.compareSync(req.body.mail+req.body.password, users[0].password)){
const token= generateToken(users[0]);
console.log(token);
res.status(200).send('operation succeed: \n' + token);
//res.statut(200).redirect('/logged');
}
else{
res.status(500).send('wrong password or mail');
}
})
.catch(err =>{
res.send(err);
});
},
}
my generateToken function:
function generateToken(user){
const payload={
iat:moment().unix(),
exp:moment().add(14,'days').unix(),
iss:user.mail,
sub:user.password
}
return jsonwebtoken.sign(payload,'app_secret');
}
thank you for your help.

Return value from one function to another with Node.JS

I am working on a login interface using the MEAN stack. I have managed to get it to work using PassportJS. My problem now is I need a way to let my client-side know whether the person logging in is an admin or user(user role). These info are available from my MongoDB.
The flow of my API call is as follow :
app.post('/login', passport.authenticate('local'), authRoutes.loginCheck);
First, it runs the passport.authenticate where it calls the function below
function verifyCredentials(username, password, done) // username & password from what user provide when logging in
{
console.log('VC');
User.findOne({username: username}, function(err, user) //query Mongo
{
console.log(user); // User role is available here, in JSON format
if(user === null) // if no username in database, do this
{
console.log('Username does not exist in database');
}
else
{
user.comparePassword(password, function(err, match) // function written to compare hashed password in Mongo & password provided by user
{
if(match)
{
done(null, {id: username, name: username});
return user; // this is not the correct syntax, but the idea is, I want to send over the user details here, so I can access the role later
}
else
{
done(null, null);
}
});
}
});
}
The verifyFunction is called with this syntax.
passport.use(new LocalStrategy(verifyCredentials));
Once that function is successfully called, the server executes the 2nd part of it which is the loginCheck.
module.exports.loginCheck = function(req, res)
{
console.log('Calling loginCheck route');
// I generate some sort of jwt token here
// payload, body, blah blah blah ...
console.log(req.body);
res.json({
authenticated: req.isAuthenticated(), //built-in authentication function, returns true or false
token: token // sends over token
role: user.role // want to send over something like this
}); // sends all these to client side as JSON
}
Since both functions are in different files, I am unclear if I have to require something or simply just pass an extra parameter to the loginCheck function. I have tried the latter though and it did not work.
One way that I could think of is do another Mongo query in the loginCheck function, but that would be kinda redundant.
Even a specific keyword for me to google up would definitely be of big help as don't I know what I should be looking for. The reason is because I am new to NodeJS, thus I am not familiarize with most of the terms yet.
I think these codes should suffice but if I am needed to provide more, let me know and I will do so. Thanks in advance !!
To pass control to next matching route you need to use next that passes as third argument in the routes:
function verifyCredentials(req, res, next) {
User.findOne({username: req.body.username}, function(err, user) //query Mongo
{
if(user === null) {
return next(new Error('Username does not exist in database'));
} else {
user.comparePassword(req.body.password, function(err, match) {
if(match) {
next(null, {id: username, name: username});
} else {
next(new Error('not match'));
}
});
}
});
}
app.post('/login', verifyCredentials, authRoutes.loginCheck);

Resources