Validating email and username in clean style - node.js

I need to validate a username (if one is provided) and an email (if one is provided) before saving to the db. I'm using MongooseJS, However, I'm not sure how to structure my code
Here is what I have so far:
var user = new User();
if(req.body.email) {
User.findOne({"email" : req.body.email}, function(err, found){
if(err) return next(err);
if(found) return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
});
}
if(req.body.username) {
User.findOne({"username" : req.body.username}, function(err, found){
if(err) return next(err);
if(found) return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
});
}
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
but of course this won't work as the user.save will be executed before either of the validation blocks execute. I realise I could put user.save inside the callbacks but then I would be repeating code which I want to avoid.

The best thing to do is to add validation on to your mongoose Schema and let it do it automatically whenever you attempt to save a model (or you can call the validation function yourself earlier). Like so:
var userSchema = new Schema({
email: {
type: String,
required: true,
validate: {
validator: function(v) {
var emailRegexp = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*#([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
return emailRegexp.test(v);
},
message: "{VALUE} does not appear to be a valid email address."
}
}
});
Then when you try to save it with incorrect data:
var user = new User({ email: "this_isnt_a_proper_email" });
user.save(function(err) {
if (err.name === "ValidationError") { // check that it comes from mongoose validation
console.log(err.errors.email.message)
res.status(400).send({ validationError: err }); // send "Bad Request" HTTP header
} else {
res.send(user) // status 200 is implicit when not set
}
});
To better organise the code that checks whether the username or email is already set in the database, I'd suggest looking up Bluebird (or similar) Promises, so that you can have a logical flow to the process.

Another simple way maybe
var user = new User();
var email = req.body.email || '';
var username = req.body.username || '';
User.find({
$or: [{"email": email}, {"username": username}]},
function(err, users){
if(err) return next(err);
if(users && users.length == 0) {
// save new user if none is matched
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
} else if (users && users.length > 0) {
// check users returned to determine which of following two error codes should be returned
//return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
//return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
}
});
You could do it through Promise, here is one sample codes by using Q.js
function findUserByEmail() {
var deferred = Q.defer();
if(req.body.email) {
User.findOne({"email" : req.body.email}, function(err, found){
if(err) return deferred.reject(err);
if(found) {
res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
deferred.reject();
}else {
// no user is found, resolve it
deferred.resolve();
}
});
} else {
deferred.reject();
}
return deferred.promise;
}
function findUserByName() {
var deferred = Q.defer();
if(req.body.username) {
User.findOne({"username" : req.body.username}, function(err, found){
if(err) return deferred.reject(err);
if(found) {
res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
deferred.reject();
} else {
// no user is found, resolve it
deferred.resolve();
}
});
} else {
deferred.reject();
}
return deferred.promise;
}
Q.all([findUserByName(), findUserByEmail()]).then (function() {
// in the resolve function, namely, no exist username and email
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
});

Related

How to compare input and Document in mongoDB

When I Register [ input Email / password to DB ] success. Then, I want to login. If input[email/pass] == document in collection >> go to next page, else console.log['wrong email/pass']
I try to wirte IF/else code but I don't know check condition.
This code is Register form
app.post('/register',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let myobj = { Email: req.body.email, Password: req.body.psw } ;
dbo.collection("Register").insertOne(myobj, function(err, res) {
if (err) throw err;
console.log(" document inserted");
db.close();
});
});
});
This code is Login form
app.post('/index',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let cursor = dbo.collection('Register').find();
cursor.each(function(err,doc) {
if (doc == req.body.email && req.body.psw){
console.log("goto next page");
}
else{
console.log('wrong');
}
});
db.close();
});
});
Correct input and wrong input Output is = Wrong
Pls insert loop check all of array pls.
app.post('/index',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("project");
dbo.collection("Register").findOne({}, function(err, result) {
if (result.Email == req.body.email && result.Password == req.body.psw) {
console.log("OK");
}
else{
console.log(result.Email && result.Password);
}
db.close();
});
});
});
You have to compare individual values, like so:
if (doc.Email == req.body.email && doc.Password == req.body.psw){
console.log("goto next page");
}
Firstly you should check for valid request body and the you should do a fineone query instead of running a for-loop and checking. see the corrected one below :
app.post("/index", function(req, res) {
let {
email,
psw
} = req.body;
if (email && psw) {
console.log("wrong credentials");
return;
} else {
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let data = dbo.collection("Register").findOne({
Email: email,
Password: psw
});
if (data) {
console.log("goto next page");
} else {
console.log("wrong");
}
db.close();
});
}
});
I'm late to the party but I just found a solution to a similar problem and wanted to share.
If you have input values in javascript and want to use them in a mongodb query you need to make them in to strings.
Assuming user._id is a value coming from a javascript function call.
This will work:
{ userId: { $eq: ${user._id} } } ✅
This won't work:
{ userId: { $eq: user._id } } ❌

passport-local-mongoose setPassword not working

Am trying to reset the user password, and am using passport-local-mongoose, and i send the reset link through mail for them and a token is added to the link also. the link opens but when user hits the reset button it throws up an error in my command line null
passortLocalMongoose = require("passport-local-mongoose")
var UserSchema = new mongoose.Schema({
email: {type: String, unique: true},
password: String,
resetPasswordToken: String,
resetpasswordExpires: Date
})
UserSchema.plugin(passortLocalMongoose);
mongoose.model("User", UserSchema )
app.post('/reset/:token', function(req, res, next) {
asynco.waterfall([
function(done) {
User.findOne({resetPasswordToken :req.params.token, resetPasswordExpires: { $gt: Date.now()}}, function(err, user) {
if(!user){
console.log(err);
req.flash('error', 'Password reset token is invalid or has expired')
return res.redirect('back')
}
if(req.body.password === req.body.confirm) {
user.setPassword(req.body.password, function(err,user){
if(err){
console.log(err)
req.flash('error', 'Sorry something went wrong')
return res.redirect('back')
} else {
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err){
req.login(user, function(err){
done(err,user)
})
})
}
})
} else {
req.flash('error', 'Passwords do not match')
res.redirect('back');
}
})
}
])
})
The error am getting when user hits the submit button
null
if i debug it just after the if(req.body.password == req.body.confirm) and type arguments it shows the two inputs are null but i type req.body it shows the two passwords
You forgot the semicolon. Is that your problem ?
app.post('/reset/:token', function(req, res, next) {
asynco.waterfall([
function(done) {
User.findOne({resetPasswordToken :req.params.token, resetPasswordExpires: { $gt: Date.now()}}, function(err, user) {
if(err){
console.log(err);
req.flash('error', 'Password reset token is invalid or has expired')
return res.redirect('back')
}
if(req.body.password === req.body.confirm) {
user.setPassword(req.body.password, function(err,user){
if(err){
console.log(err)
req.flash('error', 'Sorry something went wrong')
return res.redirect('back')
} else {
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err){
req.login(user, function(err){
done(err,user)
})
})
}
});
} else {
req.flash('error', 'Passwords do not match')
res.redirect('back');
}
});
}
])
})
Based on your information and code I can help you with assumptions only, provide more information and I'll update the answer.
Are you sure that user is found with your criteria?
User.findOne({
resetPasswordToken: req.params.token,
resetPasswordExpires: { $gt: Date.now()}
}, function(err, user) {
if (err) {} // Handle the error
if (!user) {} // User is not found, but no error as well
// only then try to call setPassword
});
I bet, your criteria is wrong and that is the reason why Mongo returns null instead of User record.
All you need to do to solve the problem is to debug that everything is writes in database and that your criteria is able to match the correct user. Everything else will work then.

Mongoose - use findOne multiple times at once

Disclaimer: I am a newb web dev.
I am creating a registration page. There are 5 input fields, with 3 of them (username, password, and email) requiring that they pass various forms of validation. Here is the code:
router.post('/register', function (req, res, next) {
user.username = req.body.username;
user.profile.firstName = req.body.firstName;
user.profile.lastName = req.body.lastName;
user.password = req.body.password;
user.email = req.body.email;
User.findOne({email: req.body.email}, function(err, existingEmail) {
if(existingEmail) {
console.log(req.body.email + " is already in use")
} else {
User.findOne({username: req.body.username}, function(err, existingUsername) {
if(existingUsername) {
console.log(req.body.username + " is already in use");
} else {
user.validate({password: req.body.password}, function(err) {
if (err) {
console.log(String(err));
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/')
}
})
}
});
}
});
}
});
});
Basically it first checks to see if it is a duplicate e-mail; if it is a duplicate e-mail, it says so in the console.log. If it isn't a duplicate e-mail, it then checks the username.... and then goes onto the password.
The issue is that it does this all one at a time; if the user inputs an incorrect email and username, it will only say that the email is incorrect (it won't say that both the email and username are incorrect).
How can I get this to validate all 3 forms at the same time?
You can use async to run them in parallel and it will also make your code cleaner and take care of that callback hell:
var async = require('async');
async.parallel([
function validateEmail(callback) {
User.findOne({email: req.body.email}, function(err, existingEmail) {
if(existingEmail) {
callback('Email already exists');
} else {
callback();
}
}
},
function validateUsername(callback) {
User.findOne({username: req.body.username}, function(err, existingUsername) {
if(existingUsername) {
callback('Username already exists');
} else {
callback();
}
}
},
function validatePassword() {
user.validate({password: req.body.password}, function(err) {
if(err) {
callback(err);
} else {
callback();
}
}
}
], function(err) {
if(err) {
console.error(err);
return next(err);
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/');
}
});
}
}
);
This way, all the validation methods inside the array will be run in parallel and when all of them are complete the user will be saved.
If you use else statements, you choose to make checks individually (one at a time) by design.
To achieve an 'all at once' behaviour, I would not use else statements (where possible, i.e. errors ar not fatal for next checks), but would do all tests in the same block, and would fill an object like this:
errors: {
existingEmail: false,
existingUserName: false,
invalidUserName: false,
wrongPassword: false,
...
};
And then I'd use it in the form to show user all errors together...
Something like this:
var errors = {};
if (existingEmail) {
console.log(req.body.email + " is already in use");
errors.existingEmail: true;
}
User.findOne({username: req.body.username}, function(err, existingUsername) {
if (existingUsername) {
console.log(req.body.username + " is already in use");
errors.existingUsername: true;
} else {
user.validate({password: req.body.password}, function(err) {
if (err) {
console.log(String(err));
errors.invalidUsername = true;
} else {
user.save(function(err, user) {
if (err) {
return next(err);
} else {
return res.redirect('/')
}
})
}
});
}
});

async.series() continues to execute next functions despite errors

I'm using the async.series() control flow from caolan's async module. U
nlike the doc's explanation that it should excute all functions in sequence, one after the other and stop when one calls its callback with an error; mine actually calls the main callback if one of the functions gets an erro but then happily continues to execute the rest of the functions in sequence.
async.series([
function (cb) {
if (!req.body.join_firstname || !req.body.join_lastname || !req.body.join_email || !req.body.join_password) {
req.flash('error', 'Please enter a name, email and password.');
cb(true);
}
if (req.body.join_password !== req.body.join_passwordConfirm) {
req.flash('error', 'Passwords must match.');
cb(true);
}
if (req.body.join_email !== req.body.join_emailConfirm) {
req.flash('error', 'Emails must match.');
cb(true);
}
cb(null);
},
function (cb) {
keystone.list('User').model.findOne({
email: req.body.join_email
}, function (err, user) {
if (err || user) {
req.flash('error', 'User already exists with that email address.');
cb(true);
}
cb(null);
});
},
function (cb) {
var userData = {
name: {
first: req.body.join_firstname,
last: req.body.join_lastname
},
email: req.body.join_email,
password: req.body.join_password
};
var User = keystone.list('User').model,
newUser = new User(userData);
newUser.save(function (err) {
if (err) {
//if there's an error, don't send activation mail
cb(err);
} else {
newUser.activationEmail(function (err) {
if (err) {
//if we can't send activation email,
//delete user from db to prevent re-registration failing because of non-unique email
keystone.list('User').model.findOne({
email: req.body.join_email
}).remove(function (err) {
req.flash('error', "Couldn't send an activation email. Contact support if this problem persists.");
cb(true);
});
} else {
cb(err);
}
});
}
});
}
], function (err) {
if (err) return next();
req.flash('success', "Hi, " + req.body.join_firstname + "! We've sent you an activation email. Please check your inbox and spam folder.");
return res.redirect('/');
});
For example, when I purposely enter the wrong password confirm value, it throws an error, executes the callback and return next(); and then just continues, even saving the user in the db. Obviously that was not the intended result.
Anyone got an idea what I'm doing wrong here?
If you want execution of a current function to stop, it's not enough to call a callback. For instance:
function(cb) {
if (!req.body.join_firstname || !req.body.join_lastname || !req.body.join_email || !req.body.join_password) {
req.flash('error', 'Please enter a name, email and password.');
cb(true); // will add this callback to the stack
}
// continuing here
// ...
}
Either change your if-then-construct:
function(cb) {
if (!req.body.join_firstname || !req.body.join_lastname || !req.body.join_email || !req.body.join_password) {
req.flash('error', 'Please enter a name, email and password.');
cb(true); // will add this callback to the stack
} else if (req.body.join_password !== req.body.join_passwordConfirm) {
//...
}
// no more code here
}
or return:
function(cb) {
if (!req.body.join_firstname || !req.body.join_lastname || !req.body.join_email || !req.body.join_password) {
req.flash('error', 'Please enter a name, email and password.');
return cb(true); // will add this callback to the stack and return
}
// will only be executed if the if is false
// ...
}

Check if document already exists if not create one

Im learning expressjs + mongo. I want to check after user logs in with passport through Steam if his data is already in database if not to create a record for him.
For this I created a static method in schema. Unfortunatelly i can't save from the inside of it.
TypeError: Object # has no method 'create'
SteamAccountSchema.statics.checkAccount = function(identifier){
this.findOne({ 'identifier' : identifier }, function(err, account){
if(err) throw err;
console.log("Checking account:" + account)
if(account) {
console.log("user already in db")
return true
} else {
console.log("Creating new user account")
this.create({
name : 'username',
identifier: identifier
}, function(err){
if(err) throw err;
// if (err) return done(err);
return false
});
}
});
}
Just cache the this object. I.e. in the code below self points to what you need:
SteamAccountSchema.statics.checkAccount = function(identifier){
var self = this;
this.findOne({ 'identifier' : identifier }, function(err, account){
if(err) throw err;
console.log("Checking account:" + account)
if(account) {
console.log("user already in db")
return true
} else {
console.log("Creating new user account")
self.create({
name : 'username',
identifier: identifier
}, function(err){
if(err) throw err;
// if (err) return done(err);
return false
});
}
});
}

Resources