how to compare password in passport-local-mongoose - node.js

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

Related

User.findOne returns null

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 ?

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.

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

Autologin with node.js, express and DaftMonk/generator-angular-fullstack

Please, help to figure out the best way to autologin user with passport.js local in MEAN stack applications by DaftMonk https://github.com/DaftMonk/generator-angular-fullstack
I want to login user right after saving him. The following code doesn't help
passport.authenticate('local', function (err, user, info) {
var error = err || info;
if (error) return res.json(401, error);
if (!user) return res.json(404, {message: 'Something went wrong, please try again.'});
var token = auth.signToken(user._id, user.role);
res.json({token: token});
})(req, res, next)
Thanks!
Are you tried to review signup module? Its login the user automatically after registering.

sails session writing bug

I'm using sails 0.10.4 and stumbled with one pretty annoying bug. When user logs in I write his data into the req.session.user then in policies I can retrieve his data such as his role, password etc. But the req.session.user becomes undefined when I go out of the login action. Do you have any ideas how to handle this? Here's the code:
api/controllers/User.js :
module.exports = {
login: function (req, res) {
Users.findOneByEmail(req.param('email'))
.exec(function (err, user) {
if ((err) || (!user)) {
res.send({
error: 'User not found'
});
return;
}
if (!passwordHash.verify(req.param('password'), user.password)) {
res.send({
error: 'Incorrect passwpord'
});
return;
}
req.session.user = user;//I write user into the session
res.send({
user: user
});
});
}
}
api/policies/isLoggedIn.js
module.exports = function (req, res, next) {
if (req.headers.authentication) {
var credentials = JSON.parse(req.headers.authentication);
if(req.session.user.login === credentials.login)//User doesn't exist in session
return next();
}
}
In a testing environment , this issue can happen when testing with Supertest and not defining an agent
var agent = request.agent(app);
agent.post('/api/login',{email:'foo#bar.com',password:'foobar})
.end(function(err,res){...; done();});
It is the correct way to work with sessions, simply using request.post would not work as it would reinit the session variable as soon as the response is sent, even if we are chaining requests inside the same test.
Learnt it the hard way, so I hope it can help some lost developper.

Resources