How to resolve Mongoose broken authentication - node.js

Hey this should be very simple for you nodejs gods, I am trying to make authentication system with nodejs using mongoose so the server is successfully receiving the email and parameters entered in the front-end but it seems as if somewhere in my in my logic I am not doing everything properly can I please get some assistance in handling this error because what happens when I console log on the back-end I get the following.
User Successfully Found
EMAIL: test1#gmail.com
PASSWORD: test1
SIGNINUSER: undefined
I get that User Successfully found even when I entered a wrong user
**Interesting part is when I remove the .then I get back the user object but return errors with regards to unhandled promise
Code below where I am handling the signing in of users
router.post("/signin", async (request, response) => {
const signinUser = await User.find({
email: request.body.email,
password: request.body.password,
})
.then((response) => {
console.log("User Successfully Found");
})
.catch((error) => {
console.log("User Does not exist");
});
//Here I was trying to check if I really am receiving the data from the client
//Just to find that I am receiving the clients data
console.log("EMAIL: ", request.body.email);
console.log("PASSWORD: ", request.body.password);
//Here I was trying to check if the usersInfo is being set inside the siginUser variable
//just to find that I getting the value of undefined
console.log("SIGNINUSER: ", signinUser);
if (signinUser) {
response.status(200).json({
_id: signinUser.id,
name: signinUser.name,
email: signinUser.email,
isAdmin: signinUser.isAdmin,
token: getToken(user),
});
} else {
response.status(401).send({ message: "Invalid Email or Password" });
}
});

Without running the code I would say you are mixing await with then and monoogose queries. So in the proposed solution User.find() returns the query (which is not a promise but a theneable), you exec it to get a promise and await for result. Removing then but keeping your code behavior might look like.
router.post("/signin", async (request, response) => {
const signinUser = await User.find({
email: request.body.email,
password: request.body.password,
}).exec();
if (!signinUser) {
console.log("User Does not exist");
return response.status(401).send({ message: "Invalid Email or Password" });
}
console.log("User Successfully Found");
console.log("EMAIL: ", request.body.email);
console.log("PASSWORD: ", request.body.password);
console.log("SIGNINUSER: ", signinUser);
return response.status(200).json({
_id: signinUser.id,
name: signinUser.name,
email: signinUser.email,
isAdmin: signinUser.isAdm
token: getToken(user),
});
});
I hope it helps.
More info here
Mongoose - What does the exec function do?

Just change your response Code i think this problem will be Gone
return response.status(200).json({
_id: signinUser.id,
name: signinUser.name,
email: signinUser.email,
isAdmin: signinUser.isAdm,
token: getToken(user.toObject()),
});
I changed Only
token: getToken(user.toObject()),

Related

NodeJS cannot import custom module although it exists

Note: What you see below is the updated description of my problem, because I have been going down a rabbit hole and finding the root cause of a problem.
So, I found what's causing it (read 'OLD DESCRIPTION' below to know the context), but I have zero idea why is it being caused. So, the thing is, apparently Node cannot find the utils.getHash function (I have a separate file called utils.js which exports the getHash function), so it is never called, and execution never moves forward.
utils.js
...
const getHash = (password) => {
return crypto.createHash('sha3-512').update(password).digest('hex')
}
...
module.exports = {
getHash: getHash
}
Someone help please :(
OLD DESCRIPTION
There's a weird problem I am facing. I wrote a backend API server in ExpressJS, and one of the task it performs is user authentication. I am using MongoDB as the database, and Mongoose to connect and perform operations on it.
The problem I am facing is that the checkUserCreds function does not proceed after a point (commented in code), and Express just returns a blank JSON response.
And I say it it's weird, because I tested with the SAME code just 2 days back, it worked correctly like it should.
user.js
userSchema.statics.checkUserCreds = function (email, password) {
return new Promise((resolve, reject) => {
// Execution goes upto '$and' line, then it goes nowhere; no exceptions are raised
User.findOne({
$and: [{ email: email }, { password: utils.getHash(password) }]
}, (err, userDoc) => {
if (err) {
reject({ status: "ERROR", message: err })
} else if (userDoc) { // If valid credential
console.log(`User with email '${email}' logged in`)
resolve({ status: "OK", message: "Login successful!" })
} else { // If invalid credential
reject({ status: "ERROR", message: "Invalid credential!" })
}
})
})
}
api.js
// Route - Login (POST: email, password)
router.post("/login", (req, res) => {
// If user is already logged in, reject further login
if (req.session.email) {
res.json({ status: "ERROR", message: "Already logged in!" }).status(403).end()
} else {
// Get data from body
var form = formidable()
form.parse(req, (err, fields, files) => {
if (err) {
res.json({ status: "ERROR", message: err }).status(500).end()
} else {
// Check if credentials are valid
User.checkUserCreds(fields.email, fields.password).then((result) => {
// This portion of code isn't reached either
req.session.email = fields.email
res.json(result).status(200).end()
}).catch((err) => {
res.json(err).status(401).end()
})
}
})
}
})
Can anyone tell me why this is happening?

Why is object returned from database call available only once?

I am running a database call (Node.js) using a promise as in my code below. The call functions properly to return information from the database. However I am immensely confused as to why I cannot then assign this value to another variable and access the information again?
My code:
app.post('/members/pages/callup', jsonParser, function (req, res) {
console.log("I RECEIVED FROM CLIENT THE FOLLOWING:");
console.log(req.body);
db.lookupMember(req.body.name)
.then(function(foundUser) {
var user = foundUser; //save the retrieved database info to a variable named 'user'...
console.log('Async success!', foundUser); //prints to console fine, the info returned from database call
console.log('Member Retrieved...' + user); //prints [object, Object] ...WHY...?????
if (typeof foundUser != "undefined") {
//do stuff
} //'foundUser' is NOT 'undefined'...
})
.catch(function(error) {
console.log('UNABLE TO RETRIEVE MEMBER INFORMATION FROM THE DATABASE...' + error);
res.redirect('/'); //route to splash page...
});
})
Here is my output to illustrate my confusion...the value returned from the database call ('foundUser') prints to console just fine, however the assigned variable on the next line just prints 'object Object'...what is the reason for that?
I RECEIVED FROM CLIENT THE FOLLOWING:
{ name: 'Admin', presence: 'Yes', str: 'Some string: &=&' }
Async success! { email: 'me#somehost.com',
username: 'Administration',
service: 'unknown',
status: 'active',
login: '2019-12-9-13-52',
legacy: '2019-12-9',
hits: 0,
token: 'UFei9NzA' }
Member Retrieved...[object Object]
Any information to clear up my confusion is appreciated. I thank you in advance.

Check for existing user using Mongoose

I'm trying to write a middleware function that (when a POST request is made with a username/password) checks to see if the user being created already exists in the database. I don't know if I'm doing this properly though.
User.find({ username: req.body.username }) returns an object which contains (or does not contain) the user if it exists...but how to properly return to exit if a user under the same username is found? Whenever I test this with Mocha, res.body.msg comes up as undefined.
Code:
module.exports = exports = function(req, res, next) {
User.find({ username: req.body.username }, (err, user) => {
if (err) return handleDBError(err, res);
if (user) return res.status(200).json({ msg: 'an account with this username already exists' });
});
next();
};
User Schema:
var userSchema = new mongoose.Schema({
username: String,
authentication: {
email: String,
password: String
}
});
give it a try very initial create a function to get the user response
function findUser(arg, callback) {
/* write your query here */
return callback(pass response here)
}
And then use it where you want
findUser(arg,function(callbackResponse) { /*do something*/ })
Since nodejs is asynchronous, chances are that the response is being sent after you are trying to read it. Make sure to keep the reading process waiting untill response is sent. I personaly use passport for handling that.

How to handle and return errors in an API using NodeJS / Express

Im creating an a API using NodeJS with the express framework and mongodb to store my data.
I have a register function which does 3 main things.
Creates the new.
Creates a token and associates it with a user.
Sends an email.
module.exports.register = function(req, res) {
var input = req.body;
var token = uuid.v4();
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
});
}
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
});
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
Now even if there are errors whilst creating the user or the token or sending an email. It's always going to return that it successfully registered a user. How can i restructure / handle this better?
I also have the problem where if the email fails to send the user and token will have already been created, how do i solve this issue? Would i just create a resend activation function?
You mention that it's always going to return that it successfully registered a user. It will also send the email even if the token creation failed.
One (not very pretty) way to do it would be to continue with the next step inside the callback function of the previous step:
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Create User'});
} else {
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) {
return res.json({success: false, errors: 'Failed To Save User Token'});
} else {
// Send Email To User
var mailData = {
from: 'deleted#example.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) {
return res.json({ success: false, errors: 'Failed To Send Email' });
} else {
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
}
});
}
});
}
}
});
As you can see, it looks like a callback-piramyd-of-doom very fast, but it only sends the success response when all the previous steps have completed.
You should also add the else case when the newToken is not created.
You should remove final return statement (from the end of your code) and return at the correct place inside each callback if there is no error.
If you send your response in the body of the function your callbacks will never get the chance to run. Therefore you must nest your callbacks and only call res.send if you are
returning early due to an error or
if everything is complete.
e.g.
// Create a new user
var user = new User ({
username: input.username,
email: input.email,
password: input.password,
active: false
});
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Create User'});
// Create a new Token
var newToken = createToken('new', null, user._id);
// Assign New Token To New User
if(newToken) {
user.tokens.push(newToken._id);
user.save(function(err) {
if(err) return res.json({success: false, errors: 'Failed To Save User Token'});
// Send Email To User
var mailData = {
from: 'deleted#hotmail.com',
to: input.email,
subject: 'Activate Your Account',
text: 'http://localhost:8080/api/auth/activate/' + token
}
mail.messages().send(mailData, function(err, body) {
if(err) return res.json({ success: false, errors: 'Failed To Send Email' });
return res.json({
success: true,
status: 'Successfully Registered User, Check Email To Activate'
});
});
});
}
});
Asynchronous alternatives
Unfortunately, with node.js you should get used to and understand callbacks; even if you end up using something else most of the time. The way your code was structured was neater and logical but does not work in node.js because you have to wait for the callbacks to complete before you can return from your function.
However, callbacks are the default but one of the worst mechanisms for handling asynchronous logic. If you want to structure the code differently you have quite a few options. Here are just a couple:
Use promises instead of callbacks.
In your case your database library (mongoose? sequelize?) should have something built in that allows you to write your code like this:
user.save()
.then(function () {
// step 1
})
.then(funciton () {
// step 2
})
.done()
This style of programming is well worth learning and will make your code more readable than callbacks. callbacks vs promises
Use Koa instead of express.
Koa, is the next generation of express written by the same people. It uses generators instead of callbacks which means you can write code that looks more like this:
// this is just an example
var result = user.save();
if (result.error) return res.send({success : false, ...});
user.token = getNewToken();
user.update();
if (result.error) return res.send({success : false, ...});
return res.send({success : true, message : "Good news, no errors"});
Generators/(aka async functions) are the direction Javascript is moving in but there is a learning curve to start using. Behind the scenes there is something very complex going on to make asynchronous code appear exactly like synchronous code. Basically, the functions know how to pause execution until they are required again.
Start with callbacks
Like I say, callbacks are not that nice. However, you should get used to using them. They are the basic building blocks of node.js and it take a while to get comfortable with better alternatives. It's also important to get used to them because otherwise you won't appreciate why the alternatives are better.
Good luck and watch out for callbacks inside loops :)

Sending JSON error from Node.js backend to iPhone frontend: "Error: Can't set headers after they are sent."

I'm new to Node.js. I'm using it as a server backend to an iPhone client. I'm calling a POST with the JSON: {firstname: "bob", email : bob#someemail.com}
The node.js code looks like this (using Express and Mongoose):
var User = new Schema({
firstname : { type: String, required: true}
, email : { type: String, required: true, unique : true}
});
var User = mongoose.model('User', User);
And for the POST,
app.post('/new/user', function(req, res){
// make a variable for the userData
var userData = {
firstname: req.body.firstname,
email: req.body.email
};
var user = new User(userData);
//try to save the user data
user.save(function(err) {
if (err) {
// if an error occurs, show it in console and send it back to the iPhone
console.log(err);
res.json(err);
}
else{
console.log('New user created');
}
});
res.end();
});
Right now, I'm trying to create duplicate users with the same email. I expect this to throw an error due to the "unique" constraint I have on the email -- which it does.
However, the node.js process dies with, "Error: Can't set headers after they are sent."
I would like to be able to send a message back to the iPhone client in scenarios such as these. For example, in the above, I'd like to be able to send back JSON to the iphone saying the result of the new user creation (successful or failed).
Thank you!
It's because the asynchronous nature of your code. The res.end() runs before the callback function of user.save you should put the res.end()inside that callback ( at the end).
this way:
user.save(function(err) {
if (err) {
// if an error occurs, show it in console and send it back to the iPhone
console.log(err);
return res.json(err);
}
console.log('New user created');
res.end();
});
Send your error using an appropriate http status, you have plenty of 4xx to do that.
res.json(420, err);
That way, you will just have to parse the message in your http fetch, with jquery it gives something like :
jQuery.ajax({
...
error: function (xhr, ajaxOptions, thrownError) {
if(xhr.status == 420) {
JSON.parse(xhr.responseText);
}
}

Resources