Check for existing user using Mongoose - node.js

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.

Related

How to resolve Mongoose broken authentication

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()),

Why is passport.authenticate needed in a registration?

I'm learning Passport, using local strategy and Mongoose, through passport-local-mongoose plugin.
It makes sense to me in a /login for example, but I don't understand why passport.authenticate is needed in a registration, right after it having already taken place...
Can anyone explain me, please? Also, what if it fails?
The following code was taken from a tutorial, but I have found similar constructions all over.
router.post('/register', function(req, res) {
User.register(new User({ username : req.body.username }),
req.body.password, function(err, user) {
if (err) {
return res.status(500).json({err: err});
}
if (req.body.firstname) {
user.firstname = req.body.firstname;
}
if (req.body.lastname) {
user.lastname = req.body.lastname;
}
user.save(function(err,user) {
passport.authenticate('local')(req, res, function () {
return res.status(200).json({status: 'Registration Successful!'});
});
});
});
});
The password isn't used to authenticated the user inside this snipped.
Call User.register
Creates a new User Object with the username, and needs the password to do
some magic with it. Probably build a hash which gets added to your 'user' object.
Call user.save
Saves this 'user' object inside your Storage, if this is successful the user gets authenticated, and returns HTTP 200 which ends the registration process.
Error Handling isn't implemented in this save method, for that you will have an exception on error.
Hope this helps to understand, your snippet.
To use your /login method your new user can use his username and password which he gave to you during the registration process.

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.

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

Can't access data from a POST call by express saved by Mongoose

I am just learning Node, Mongoose and Express. I've looked all through stackoverflow and else where, and I still cannot find an answer to my question.
I'm fairly certain this a just a very beginner mistake. When I save posted data inside of app.post, I cannot access it anywhere else in my node/express code. I get the error "ReferenceError: getUsersInfo is not defined."
var mongoose = require('mongoose');
mongoose = mongoose.createConnection('localhost', '27017');
mongoose.on('error', console.error.bind(console, 'connection error:'));
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectID;
var usersSchema = new Schema({
username: String,
password: String,
email: String,
date_created:{ type: Date, default: Date.now }
});
var users = mongoose.model('users', usersSchema);
app.get('/', function(req,res){
res.render('layout.jade', {
title : "Hello!"
,analytics: 'XXXXXX'
});
});
app.post('/', function(req, res){
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body.email,
});
getUsersInfo.save(function (err, getUsersInfo) {
if (err){throw err;}
console.log(getUsersInfo.username);
res.redirect("/success");
});
});
app.get('/success', function(req, res){
res.render('loggedin.jade', {
title : "Admin Panel",
analytics: 'XXXXXX'
});
var username = getUsersInfo("username");
res.write("<h1> hi, " + username + "</h1>");
});
Any help you can give me such as places to look or a better way to write my code would be much appreciated. I tried learning from out-dated tutorials and haven't gotten anywhere and the mongoose api/docs don't seem to cover this thing.
Thanks!
EDIT
To clarify what I'm trying to achieve here is I want to take form inputs (rendered in the layout.jade file) and save them to the database (Which I'm doing in app.post). I want to then be able to access those database objects elsewhere in the code, not specifically in just app.get('/success'). But I keep running into a scope issue, it seems.
In a nodejs/expressjs application, your routes (app.get, app.post etc) are set up with callback functions that will be invoked when a user requests the corresponding URL. All operations that should be done in a request needs to be invoked from within these callbacks. In your example, you're attempting to access getUsersInfo outside the callback you have set up in app.post. If you move the code inside the callback instead, it should get you further:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
res.redirect('/success');
}
else {
// Send error
}
});
});
Edit: Answer above was confused by the indentation of the code, which should be corrected now. It looks like you're making a regular web app, so I would recommend that you check out the session middleware. An easy way to get started with it is to generate your express app with the command express --sessions myapplication. This will set your application so you have a req.session object where you can put session scoped objects:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
req.session.user = userInfo;
res.redirect('/success');
}
else {
// Send error
}
});
});
Then in your app.get
app.get('/success', function(req, res) {
var user = req.session.user;
// Do what you want with the user
});
If you were using a REST approach, a typical pattern when saving and then retrieving a resource is to redirect with GET to the URL fetching the created resource, and include an identifier for the resource. In that case, your redirect would look like this
res.redirect('/success/' + userInfo.id)
And you could make a route like this:
app.get('/success/:userId', function(req, res) {
var user_id = req.param('userId');
users.findById(user_id, function(err, user) {
// user is available here. Add it to the template context and render it.
});
});
The syntax :userId in the uri indicates a path variable named userId which will be available in req.params.
But if you're writing a regular web application, I would recommend the first approach using the session middleware.

Resources