Object not defined when using await - javascript

I am currently trying to add some authentication to my node API.
Right now I'm using PassportJS (pretty new to this so sorry for my incompetents).
I am trying to add a local strategy and check if the users password is legit when loggin in:
// Local Strategy
passport.use(
new LocalStrategy(async (username, password, done) => {
try {
// Find user by username
const user = await User.findOne({ username })
// No user found
if (!user) {
return done(null, false)
}
console.log('user', user) // Getting output
// Check if password correct
const isMatch = await user.isValidPassword(password)
// Handle if password is not correct
if (!isMatch) {
return done(null, false)
}
// Return user
done(null, user)
} catch (err) {
done(err, false)
}
})
)
Something I've noticed is when using await on const isMatch = await user.isValidPassword(password) Postman is saying: Error: ReferenceError: user is not defined. And when I remove await it works fine, but I can type in the wrong password but I still can login. And I can see my user object when I console.log it.
{
"username": "martinnord3",
"password": "this_is_the_wrong_password"
}
Here's the isValidPassword function:
UserSchema.methods.isValidPassword = async function(newPassword) {
try {
return await bcrypt.compare(newPassword, user.password)
} catch (err) {
throw new Error(err)
}
}
I guess there's something obvious I'm missing, but I can't manage to solve this.
Thanks for taking your time to read this!

Well this is a bit awkward, but I guess it's my duty to answer my own dumb question... My function isValidPassword has this: ...user.password and I don't specify what user is in that function.. It expects this.

Related

Mongoose: Understanding Callbacks vs Async/Await

I am trying to understand a callbacks in mongoose. I really just want to clarify my understanding so I can be sure I am on the right track.
I understanding using async/await to do queries in mongodb using mongoose. For example, the following code will get a user from my database, assuming my model "User" is set up correctly.
const getUser = async () => {
const user = await User.find({username: "John"})
return user
}
Here is my problem. I am working through the odin project and I have came across a section where callbacks are used instead of async/await. I have read some of the mongoose documentation regarding the matter without any luck.
Here is an example I am working with:
passport.use(
new LocalStrategy((username, password, done) => {
User.findOne({username: username}, (err, user) => {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, {message:"Incorrect Username"})
}
if (user.password !== password) {
return done(null, false, {message: "Incorrect password"})
}
return done(null, user)
})
})
)
I understand most of what is going on here, but I do not understand how this is able to function without the use of async/await. I am looking specifically at the line:
User.findOne({username: username}, (err, user) => {callback})
My guess is that the database is queried, and if the user is found the data from the query is stored in the parameter "user."
If the query fails and no data is returned, then user becomes null and our err parameter will contain a message.
Is this correct?
You can use simple concept
async getUserList(){
try{
const users = User.findOne({username: username});
return users;
}catch(error){
throw error;
}
}
call function anywhere
console.log(getUserList());

SQL Injection returns injected code as it is

I'm trying to beat this CTF: a website under construction that has a simple login page where you can login or register a new user.
It uses node express and a SQLite DB.
Analyzing the source code I found this query:
getUser(username){
return new Promise((res, rej) => {
db.get(`SELECT * FROM users WHERE username = '${username}'`, (err, data) => {
if (err) return rej(err);
res(data);
});
});
},
This function gets called after a Middleware checked session cookies with jsonwebtoken to retrieve the username when you GET '/'.
This is where it's called:
router.get('/', AuthMiddleware, async (req, res, next) => {
try{
let user = await DBHelper.getUser(req.data.username);
if (user === undefined) {
return res.send(`user ${req.data.username} doesn't exist in our database.`);
}
return res.render('index.html', { user });
}catch (err){
return next(err);
}
});
AuthMiddleware:
module.exports = async (req, res, next) => {
try{
if (req.cookies.session === undefined) return res.redirect('/auth');
let data = await JWTHelper.decode(req.cookies.session);
req.data = {
username: data.username
}
next();
} catch(e) {
console.log(e);
return res.status(500).send('Internal server error');
}
}
Since that appears to be the only query formatted that way (the others all use ?) and in general the only evident vulnerability, I suspect the flag is stored somewhere in the database.
As the only way to get that function called is to have an active session i registered a user with 'malicious' sql code as the username. At first I tried to close the quote and attach an OR WHERE to get all the users:
SELECT * FROM users WHERE username = '${username}' +
bob' OR WHERE username IS NOT NULL =
SELECT * FROM users WHERE username = 'bob' OR WHERE username IS NOT NULL
This should at least throw an error as WHERE username = 'bob' OR WHERE username IS NOT NULL should return a collection with all the users in the database while it's rendered on the webpage as
Welcome {{ user.username }}
This site is under development.
Please come back later.
I was expecting at least "no username property on array" or something like that. Instead it always return the full username I gave him
Welcome bob' OR WHERE username IS NOT NULL
This site is under development.
Please come back later.
Am I missing something? Is there a way to escape eventual quotes that might be added during the cookie reading?
EDIT:
Here is the function that gets called when you attempt a login
/auth route:
router.post('/auth', async (req, res) => {
const { username, password } = req.body;
if((username !== undefined && username.trim().length === 0)
|| (password !== undefined && password.trim().length === 0)){
return res.redirect('/auth');
}
if(req.body.register !== undefined){
let canRegister = await DBHelper.checkUser(username);
if(!canRegister){
return res.redirect('/auth?error=Username already exists');
}
DBHelper.createUser(username, password);
return res.redirect('/auth?error=Registered successfully&type=success');
}
// login user
let canLogin = await DBHelper.attemptLogin(username, password);
if(!canLogin){
return res.redirect('/auth?error=Invalid username or password');
}
let token = await JWTHelper.sign({ // Maybe something can be done with this function?
username: username.replace(/'/g, "\'\'").replace(/"/g, "\"\"")
})
res.cookie('session', token, { maxAge: 900000 });
return res.redirect('/');
});
attemptLogin():
attemptLogin(username, password){
return new Promise((res, rej) => {
db.get(`SELECT * FROM users WHERE username = ? AND password = ?`, username, password, (err, data) => {
if (err) return rej();
res(data !== undefined);
});
});
}
EDIT 2.0:
I just noticed the part where it stores the session cookie:
let token = await JWTHelper.sign({
username: username.replace(/'/g, "\'\'").replace(/"/g, "\"\"")
})
It apparently replaces all ' with \'\'. I can solve half of that by escaping the quote so that it becomes \\'\'. This allows me to close the username='' statement, but I still need to find a way to invalidate the second \'.

Node and Passport Cannot Get Password to change with user.setPassword

So, I am trying to setup my program to update a user's password once the old one is confirmed correct. I have gotten all but one line of this code to function the way I expect it to:
router.put("/:id/edit_password", isLoggedIn, isAdministrator, async function(req, res){
user = {};
try {
user = await User.findById(req.params.id)
}
catch (err) {console.log(err);}
user.authenticate(req.body.user["old_password"], async function(err, model, passwordError){
if(passwordError){
console.log("Old Password is Incorrect");
// Flash Old Password is Incorrect
res.redirect("/users/" + req.params.id + "/edit_password");
} else if (model) {
console.log("Old Password is Correct");
try {
await user.setPassword(req.body.user["new_password"]); //this line does not work
console.log("Password Changed");
}
catch (err) {console.log(err);}
res.redirect("/users/" + req.params.id);
} else
{
console.log(err);
}
});
});
The await user.setPassword part of this does not seem to function. With the console.logs, I can confirm that everything is going the way it should. Is there a different function that needs to be used besides user.setPassword in this case?
Thanks.
if your schema is more or less set like this
user{ //...somedate,password{type:string,..} } you shouldset your new password like this user.Password=req.body.new_password
if you want to use passport setPassword
user.setPassword(req.body.new_password, function(err,user){
if(err){
console.log(err)
} else {
console.log("the password changed for this "+user)
}
})
So, this will not explain why setPassword did not work, but I did find out a way to get this working a different way. Instead of using setPassword(req.body.user["new_password"]), instead, I used the passport function: changePassword(req.body.user["old_password"], req.body.user["new_password"]) and this worked. The documentation I used is here:
https://www.geeksforgeeks.org/nodejs-authentication-using-passportjs-and-passport-local-mongoose/
I had the same issue. Adding the save() worked for me. Without the await user.save(), the password did not seem to work.
await user.setPassword(req.body.password, async function(err, user) {
if (err) {
console.log(err);
} else {
await user.save() ;
console.log("Password Updated!");
}
});

Unable to pass custom express-validator check

The purpose of the following code is to check whether an email already exists in MongoDB, using express-validator:
app.post('/registerPage',[check('email').custom((email) => {
// connect to database
let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://localhost';
MongoClient.connect(url, function(err, client) {
if (err) throw err;
let db = client.db('Mydatabase');
// search database
return db.collection('users').findOne({
email: email
}).then(user => {
if (user) {
console.log(user); // here console shows correct record in database
return Promise.reject('E-mail already in use');
}
// otherwise, it returns null
});
})
}).withMessage('Error Message Example')], (req, res) => {
// Handle the request
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
});
When email already exists, console shows Promise.reject('E-mail already in use');.
The problem is when email does not exist, although it doesn't show Promise.reject, the code cannot process any further, validationResult(req) is not empty, so it still prints out the error message 'Error Message Example'. But there isn't any problem with non-custom validators which can successfully pass the checks.
I tried to add an else statement where !user, it doesn't work.
The question is how to pass the custom validation check, or why the array validationResult(req) is not empty even it should be? How do I make sure validationResult is empty after all checks were passed.
The issue is you are returning the promise in the callback of MongoClient.connect and not the validator function. Try using Promise wrapper like:
app.post('/registerPage',[check('email').custom((email) => {
return new Promise((resolve, reject) => {
// connect to database
let MongoClient = require('mongodb').MongoClient;
let url = 'mongodb://localhost';
MongoClient.connect(url, function(err, client) {
if (err) throw err;
let db = client.db('Mydatabase');
// search database
return db.collection('users').findOne({
email: email
}).then(user => {
if (user) {
console.log(user); // here console shows correct record in database
return reject('E-mail already in use');
}
return resolve();
});
})
});
}).withMessage('Error Message Example')], (req, res) => {
// Handle the request
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
});
Hope this helps!

Trying to use bcrypt,compare and returns Promise { <pending>}

I am trying to create a login api by using aws lambda, mongoose, and bcryptjs.
My lambda handler uses async and I am just trying to compare the user typed password with already hashed password that is in the database by using the bcrypt.compare() function in the bcryptjs module. However, my code keeps giving me Promise { } so I have tried a bunch of ways to fix this but still have an issue. I am new to using async so I might be doing totally wrong so please do not be harsh on me :)
I am getting the user account data with the encrypted password from MongoDB atlas by using the below code and it works perfectly.
let user = await User.findOne(query).select('_id name email password');
I also have a mongoose method that I have created in a user.js file just like below.
UserSchema.methods.comparePassword = function(password) {
return bcrypt.compare(password, this.password);
};
so the above method gets called and prints the result with console.log with the following code.
let passwordValid = user.comparePassword(parameters.password);
console.log('Password is validated', passwordValid);
and it gives me
INFO Password is validated Promise { <pending> }
in the lambda console.
I have done many searches so I tried using await before comparePassword like below and still not working.
let passwordValid = await user.comparePassword(parameters.password);
I have also tried resolving the returned promise by using then() like below
let passwordValid = user.comparePassword(parameters.password);
passwordValid.then(function(err, result) {
callback(null, {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify({
"success": false,
"content": result
})
});
});
However, this still does not work as I want. Only respond I receive is
{
"message": "Internal server error"
}
Have you seen this https://www.npmjs.com/package/bcrypt?
In this line, await is neede:
return bcrypt.compare(password, this.password);
As the above link suggests:
async function checkUser(username, password) {
//... fetch user from a db etc.
const match = await bcrypt.compare(password, user.passwordHash);
if(match) {
//login
}
//...
}

Categories

Resources