How to check if user with email already exists? - node.js

I'm trying to prevent registration with a previously registered email. I tried to create a custom validation in mongoose schema. but it gave me an error ValidationError: User validation failed
at MongooseError.ValidationError. The code is down bellow. Can some one tell me where is the error or a better way to check if the user email exists in db.
// user schema
var UserSchema = mongoose.Schema({
username: {
type: String,
index: true,
require: true
},
password: {
type: String,
require: true
},
email: {
type: String,
lowercase: true,
trim: true,
index: {
unique: true,
},
validate: {
validator : isEmailExists, msg: 'Email already exists'
}
},
name: {
type: String
},
admin: Boolean,
active: Boolean,
});
// validation
function isEmailExists(email, callback) {
if (email) {
mongoose.models['User'].count({ _id: { '$ne': this._id }, email: email }, function (err, result) {
if (err) {
return callback(err);
}
callback(!result);
})
}
}
// createUser function
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
Router
router.post('/register', function(req, res, next) {
var name = req.body.name;
var email = req.body.email;
var password = req.body.password;
var confirmedPassword = req.body.confirmedPassword;
// Validation
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('email', 'Email is required').notEmpty();
req.checkBody('email', 'Email is not valid').isEmail();
req.checkBody('password', 'Password is required').notEmpty();
req.checkBody('confirmedPassword', 'Passwords do not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
res.render('register', {
errors: errors
});
} else {
var newUser = new User({
name: name,
email: email,
password: password,
admin: false,
active: false
});
User.createUser(newUser, function (err, user) {
if (err) {
throw err;
}
});
req.flash('success_msg', 'You are registerd and can now login');
res.redirect('/users/login');
}

The best way to check if the e-mail id already exists in the database or not is by using express-validator.
Since, there upgrade to version 4, the API has changed.
Now, instead of using:-
const expressValidator = require('express-validator');
..in your app.js file, and then calling the middleware. Instead, just do this in your users route file:-
const { check, validationResult } = require('express-validator/check');
Now, to check if the e-mail id already exists in the database, you will have to use Promise. Here's a working code:-
router.post('/register', [
check('name')
.not()
.isEmpty()
.withMessage('Name is required'),
check('email')
.not()
.isEmpty()
.withMessage('Email is required')
.isEmail()
.withMessage('Invalid Email')
.custom((value, {req}) => {
return new Promise((resolve, reject) => {
User.findOne({email:req.body.email}, function(err, user){
if(err) {
reject(new Error('Server Error'))
}
if(Boolean(user)) {
reject(new Error('E-mail already in use'))
}
resolve(true)
});
});
}),
// Check Password
check('password')
.not()
.isEmpty()
.withMessage('Password is required'),
// Check Password Confirmation
check('confirmedPassword', 'Passwords do not match')
.exists()
.custom((value, { req }) => value === req.body.password)
], function(req, res) {
var name = req.body.name;
var email = req.body.email;
var password = req.body.password;
var confirmedPassword = req.body.confirmedPassword;
// Check for Errors
const validationErrors = validationResult(req);
let errors = [];
if(!validationErrors.isEmpty()) {
Object.keys(validationErrors.mapped()).forEach(field => {
errors.push(validationErrors.mapped()[field]['msg']);
});
}
if(errors.length){
res.render('register',{
errors:errors
});
} else {
var newUser = new User({
name: name,
email: email,
password: password,
admin: false,
active: false
});
User.createUser(newUser, function (err, user) {
if (err) {
throw err;
}
});
req.flash('success_msg', 'You are registerd and can now login');
res.redirect('/users/login');
}
You can similarly do this to check for the username also.
Here is the link to the official GitHub page of express-validator

You can use Monsgoose's Model.findOne()=> https://mongoosejs.com/docs/api.html#model_Model.findOne:
router.post('/register',(req,res)=>{
// Object destructuring
const {username,password,email,...rest} =req.body;
// Error's Array
let errors = [];
// Mongoose Model.findOne()
User.findOne({email:email}).then(user=>{
if(user){
errors.push({msg: 'Email already exists'});
res.render('register',{errors})
}
})
})

You can use email-check package for checking whether the user had been previously registered (whether there is a duplicate email address inside email field).
Here is the link for downloading the package
https://www.npmjs.com/package/email-check
By writing unique: true property inside Models will provide mail address to be not repetitive. But you should also include email-chack validation which you can do inside Router
import emailCheck from "email-check";
//other imports
router.post("/register", (req, res) => {
var name = req.body.name;
var email = req.body.email;
var password = req.body.password;
var confirmedPassword = req.body.confirmedPassword;
// your validation for another fields
emailCheck(email)
.then(() => {
User.create(req.body)
.then(() => {
res.send(req.body);
})
.catch((error) =>
res.json({serverErrorDublicateEmail: "The email address is already subscribed. Please try to use another one or simply Log in"});
});
})
.catch(() => {
res.json({serverErrorEmailExistence: "The email address doesn't exist. Please try the valid one"});
});
});
emailCheck returns a Promise. Note: I am using ES6 syntax.
That's all. Your UserSchema can stay without any validation.

Related

How to use bcrypt.compare in sails js schema?

I have a user model like this:
module.exports = {
attributes: {
email: {
type: 'string',
isEmail: true,
unique: true,
required: true
},
password: {
type: 'string',
required: true
}
},
beforeCreate: (value, next) => {
bcrypt.hash(value.password, 10, (err, hash) => {
if (err){
throw new Error(err);
}
value.password = hash;
next();
});
},
};
Now when I want to match the password during login, how do I decrypt the password, if possible I would prefer to perform it in the user model file.
controller/ login.js
module.exports = {
login: async (req, res) => {
try{
const user = await User.findOne({email: req.body.email});
if (!user){
throw new Error('Failed to find User');
}
// here I want to match the password by calling some compare
//function from userModel.js
res.status(201).json({user: user});
}catch(e){
res.status(401).json({message: e.message});
}
},
};
first try to find the user with the given username by user
const find = Users.find(user=>user.username===req.body.username)
if(!find){
res.send('User Not Found')
}
else{
if( await bcrypt.compare(req.body.password,find.password)){
//now your user has been found
}
else{
//Password is Wrong
}
}
You must use bcrypt.compare(a,b)
a = given password by user
b = original password if username exist
hope it solve your problem

Cannot Save and Activate the Registered Email Account

I have successfully send a token in my Email upon Registration via Send Grid
router.post(
'/',
[
check('lastname', 'Lastname is required').not().isEmpty(),
check('firstname', 'Firstname is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { lastname, firstname, email, password } = req.body;
try {
// Identify if users exists
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
errors: [{ msg: 'User already exists' }],
});
}
// Get users avatar
const avatar = gravatar.url(email, {
// size
s: '200',
// rating
r: 'pg',
// default (mm = default image)
d: 'mm',
});
// create a new instance of a user
user = new User({
lastname,
firstname,
email,
avatar,
password,
});
// // Encrypt password
// // salt to do the hashing
const salt = await bcrypt.genSalt(10);
// // creates a hash and put to the user.password
user.password = await bcrypt.hash(password, salt);
const token = jwt.sign(
{
user,
},
accountActivation,
{
expiresIn: 360000,
}
);
const emailData = {
from: emailFrom,
to: user.email,
subject: 'Account Activation',
html: `
<h1>Please use the following to activate your account</h1>
<p>${PORT}/activeprofile/${token}</p>
<hr />
<p>This email may contain sensetive information</p>
<p>${PORT}</p>
`,
};
sgMail
.send(emailData)
.then(() => {
return res.json({
message: `Email has been sent to ${email}`,
});
})
.catch((err) => {
return res.status(400).json({
msg: 'Unable to send',
});
});
// await user.save()
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
and I have successfully get an Email with it's token.
whenever I am trying to verify it in my postman.
router.post('/activeprofile', (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, accountActivation, (err) => {
if (err) {
console.log('Activation error');
return res.status(401).json({
errors: 'Expired link. Signup again',
});
} else {
const { lastname, firstname, email, password } = jwt.decode(
token
);
// create a new instance of a user
const user = new User({
lastname: req.body.lastname,
firstname,
email,
password,
});
user.save((err, user) => {
if (err) {
console.log('Error Saving the User', err.message);
return res.status(401).json({ msg: 'Unable to save' });
} else {
return res.status(200).json({
success: true,
message: user,
message: 'Signup Success',
});
}
});
}
});
} else {
return res.json({
message: 'Error happened, Please try again later',
});
}});
I always get this Error.
I even tried doing
const user = new User({
lastname: req.body.lastname,
firstname: req.body.firstname,
email: req.body.email,
password: req.body.passsword,
});
still I ended up with all the same errors in the picture posted.
Btw. this is my User Schema
const UserSchema = new mongoose.Schema({
lastname: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},}); module.exports = Use = mongoose.model('user', UserSchema);
my postman error:
I manage to figure out the problem. It's because I have created a double instance of the user
so I removed the user instance from the registration
router.post(
'/',
[
check('lastname', 'Lastname is required').not().isEmpty(),
check('firstname', 'Firstname is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { lastname, firstname, email, password } = req.body;
try {
// Identify if users exists
let data = await User.findOne({ email });
if (data) {
return res.status(400).json({
errors: [{ msg: 'User already exists' }],
});
}
const token = jwt.sign(
{
lastname,
firstname,
email,
password,
},
accountActivation,
{
expiresIn: 360000,
}
);
const emailData = {
from: emailFrom,
to: email,
subject: 'Account Activation',
html: `
<h1>Please use the following to activate your account</h1>
<p>${PORT}/activeprofile/${token}</p>
<hr />
<p>This email may contain sensetive information</p>
<p>${PORT}</p>
`,
};
sgMail
.send(emailData)
.then(() => {
return res.json({
message: `Email has been sent to ${email}`,
});
})
.catch((err) => {
return res.status(400).json({
msg: 'Unable to send',
});
});
// await user.save()
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
and put it in the account activation together with the gravatar and the hashing of the passwords
router.post('/activeprofile', (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, accountActivation, async (err, decoded) => {
if (err) {
console.log('Activation error');
return res.status(401).json({
errors: 'Expired link. Signup again',
});
}
const { lastname, firstname, email, password } = jwt.decode(token);
// // Get users avatar
const avatar = gravatar.url(email, {
// size
s: '200',
// rating
r: 'pg',
// default (mm = default image)
d: 'mm',
});
// create a new instance of a user
let user = new User({
lastname,
firstname,
email,
avatar,
password,
});
// Encrypt password
// salt to do the hashing
const salt = await bcrypt.genSalt(10);
// creates a hash and put to the user.password
user.password = await bcrypt.hash(password, salt);
user.save((err, user) => {
if (err) {
console.log('Error Saving the User', err.message);
return res.status(401).json({ msg: 'Unable to save' });
} else {
return res.status(200).json({
success: true,
message: user,
message: 'Signup Success',
});
}
});
});
} else {
return res.json({
message: 'Error happened, Please try again later',
});
}});
and it's successful, the user is saved in the database.

validate user by express-validator

I am kind of new to the nodeJs and I am trying to check if user's email already exists in the database, but seems like I am doing something wrong, it always tell me that "Email already exists" I tried a few different ways but none of them work. I hope someone here will be able to help me figure this out. thank you.
router.post('/register', function (req, res) {
var name = req.body.name;
var email = req.body.email;
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('email', 'Email is required').notEmpty();
req.checkBody('email', 'Email already exists').custom(email => {
return User.getUserByEmail(email, function(user) {
if (user) {
throw new Error('this email is already in use');
}
})
});
// User.getUserByEmail(email, function (err, user) {
//
// if(!isEmpty(user)){
// console.log(user);
// MyEmitter.emit('isUser', true);
// var empty = '';
// req.checkBody('empty', 'Username/Email is already taken').isEmpty();
// }
// });
/** CKECK IF USER EXISTS NEEDS TO BE WORKED ON */
var errors = req.validationErrors();
if (errors) {
res.render('register', {
errors: errors
});
}else {
var newUser = new User({
name: name,
email: email,
username: username,
password: password
});
User.createUser(newUser, function (err, user) {
if (err) throw err;
});
req.flash('success_msg', 'Registration successful, please log in');
res.redirect('/users/login');
}
});
/** get user by email **/
module.exports.getUserByEmail = function(email, callback){
var query = {email: email};
User.findOne(query, callback);
}
Check this one:
req.checkBody('email', 'Email already exists').custom(email => {
User.findOne({email: email}).exec(function(user) {
if (!user) {
return true;
}
else{
return false;
}
})
});

Express-Validator: Validation Error Message not Appearing for custom validator

If I try to register a duplicate user Name, it all works, except for the error message saying it has already been registered. All the other error messages work; just not this one.
router.post('/register', function(req, res){
var name = req.body.name;
var email = req.body.email;
// var username = req.body.username;
var username = req.body.name;
var password = req.body.password;
var password2 = req.body.password2;
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('name', 'This name is already registered.')
.custom(function(name, req){
return new Promise((resolve, reject) => {
User.findOne({ name: name }, (err, user) => {
console.log(name);
console.log(user);
if (err) throw err;
if(user == null) {
resolve();
} else {
console.log('rejecting');
reject();
}
});
});
});
req.checkBody('email', 'Email is required').notEmpty();
req.checkBody('email', 'Email is not valid').isEmail();
// req.checkBody('username', 'Username is required').notEmpty();
req.checkBody('password', 'Password is required').notEmpty();
req.checkBody('password2', 'Passwords do not match').equals(req.body.password);
var errors = req.validationErrors();
if(errors){
res.render('register',{
errors:errors
});
} else {
var newUser = new User({
name: name,
What am I missing?
I am quite sure the way you chain custom() in your code is for the new API only. You are otherwise using the legacy API. For the legacy API you have to define the custom validators when you apply the middleware (in app.js or something similar). For example:
app.use(expressValidator({
customValidators: {
isUniqueName: function(name) {
return new Promise((resolve, reject) => {
User.findOne({ name: name }, (err, user) => {
console.log(name);
console.log(user);
if (err) throw err;
if (user == null) {
resolve();
} else {
console.log('rejecting');
reject();
}
});
});
},
},
}));
and in the controller:
req.checkBody('name', 'This name is already registered.').isUniqueName();
I would generally recommend changing to the new API. It shouldn't take too much time when you already know what you want to validate. It's written more about that here.

Best way to validate uniqueness of a field Mangoose Node

When registering a new user, I want to check email for uniqueness. I am using body parser to make sure all fields are not empty, but how am I to check that the input email is not used by anyone else and to immediately output the message for a user?
The technology stack is Node.js, mongoose, body-parser module, mongodb
Here's the route file:
router.post('/register', function(req, res){
let name = req.body.name;
let email = req.body.email;
req.checkBody('name', 'Name field is empty!').notEmpty();
req.checkBody('email', 'Email field is empty!').notEmpty();
req.checkBody('email', 'Invalid email format').isEmail();
var errors = req.validationErrors();
if(errors){
res.render('register', {
errors:errors
});
} else {
let newUser = new User({
name: name,
email: email
});
After looking through similar questions, I found a way to use a pre save in my model file, but I don't know how to sisplay the error to the user as a part of the errors array (see above)
Any help will be highly appreciated!
You can achieve both from the Mongoose model and the Register() method.
The user model should be like this:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
sparse: true
},
password: {
type: String,
required: true
},
},
{
timestamps: true
});
module.exports = mongoose.model('User', UserSchema);
This will ensure that emails are unique. Then at the register method, you do this:
exports.register = function(req, res, next){
let name = req.body.name;
let email = req.body.email;
User.findOne({email: email}, function(err, existingUser){
if(err){
return res.status(500).json(err);
}
if(existingUser){
return res.status(422).json('Email address is already registered.');
}
else {
var user = new User({
username : username,
email: email,
password: password
});
user.save(function(err, user){
if(err){
return next(err);
}
var userInfo = setUserInfo(user);
res.status(201).json({
token: 'JWT ' + generateToken(userInfo),
user: userInfo
})
});
}
});
}
}
}
Hope this helps.

Resources