Preventing Brute Force Using Node and Express JS - node.js

I'm building a website using Node and Express JS and would like to throttle invalid login attempts. Both to prevent online cracking and to reduce unnecessary database calls. What are some ways in which I can implement this?

rate-limiter-flexible package with Redis or Mongo for distributed apps and in-Memory or with Cluster helps
Here is example with Redis
const { RateLimiterRedis } = require('rate-limiter-flexible');
const Redis = require('ioredis');
const redisClient = new Redis({
options: {
enableOfflineQueue: false
}
});
const opts = {
redis: redisClient,
points: 5, // 5 points
duration: 15 * 60, // Per 15 minutes
blockDuration: 15 * 60, // block for 15 minutes if more than points consumed
};
const rateLimiter = new RateLimiterRedis(opts);
app.post('/auth', (req, res, next) => {
// Consume 1 point for each login attempt
rateLimiter.consume(req.connection.remoteAddress)
.then((data) => {
const loggedIn = loginUser();
if (!loggedIn) {
// Message to user
res.status(400).send(data.remainingPoints + ' attempts left');
} else {
// successful login
}
})
.catch((rejRes) => {
// Blocked
const secBeforeNext = Math.ceil(rejRes.msBeforeNext / 1000) || 1;
res.set('Retry-After', String(secBeforeNext));
res.status(429).send('Too Many Requests');
});
});

Maybe something like this might help you get started.
var failures = {};
function tryToLogin() {
var f = failures[remoteIp];
if (f && Date.now() < f.nextTry) {
// Throttled. Can't try yet.
return res.error();
}
// Otherwise do login
...
}
function onLoginFail() {
var f = failures[remoteIp] = failures[remoteIp] || {count: 0, nextTry: new Date()};
++f.count;
f.nextTry.setTime(Date.now() + 2000 * f.count); // Wait another two seconds for every failed attempt
}
function onLoginSuccess() { delete failures[remoteIp]; }
// Clean up people that have given up
var MINS10 = 600000, MINS30 = 3 * MINS10;
setInterval(function() {
for (var ip in failures) {
if (Date.now() - failures[ip].nextTry > MINS10) {
delete failures[ip];
}
}
}, MINS30);

So after doing some searching, I wasn't able to find a solution I liked so I wrote my own based on Trevor's solution and express-brute. You can find it here.

okk,i found the solution of max login attemp on wrong password in mongoose and expressjs.there is a solution.
*first we will define the user schema
*second we will define the max login on wrongpassword handler function.
*third when we will create the login api then we will check this function that how many times user login with wrong password.so be ready for code
var config = require('../config');
var userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: String,
verificationToken: { type: String, unique: true, required: true },
isVerified: { type: Boolean, required: true, default: false },
passwordResetToken: { type: String, unique: true },
passwordResetExpires: Date,
loginAttempts: { type: Number, required: true, default: 0 },
lockUntil: Number,
role: String
});
userSchema.virtual('isLocked').get(function() {
return !!(this.lockUntil && this.lockUntil > Date.now());
});
userSchema.methods.incrementLoginAttempts = function(callback) {
console.log("lock until",this.lockUntil)
// if we have a previous lock that has expired, restart at 1
var lockExpired = !!(this.lockUntil && this.lockUntil < Date.now());
console.log("lockExpired",lockExpired)
if (lockExpired) {
return this.update({
$set: { loginAttempts: 1 },
$unset: { lockUntil: 1 }
}, callback);
}
// otherwise we're incrementing
var updates = { $inc: { loginAttempts: 1 } };
// lock the account if we've reached max attempts and it's not locked already
var needToLock = !!(this.loginAttempts + 1 >= config.login.maxAttempts && !this.isLocked);
console.log("needToLock",needToLock)
console.log("loginAttempts",this.loginAttempts)
if (needToLock) {
updates.$set = { lockUntil: Date.now() + config.login.lockoutHours };
console.log("config.login.lockoutHours",Date.now() + config.login.lockoutHours)
}
//console.log("lockUntil",this.lockUntil)
return this.update(updates, callback);
};
here is my login function where we have checked the max login attempt on wrong password.so we will call this function
User.findOne({ email: email }, function(err, user) {
console.log("i am aurhebengdfhdbndbcxnvndcvb")
if (!user) {
return done(null, false, { msg: 'No user with the email ' + email + ' was found.' });
}
if (user.isLocked) {
return user.incrementLoginAttempts(function(err) {
if (err) {
return done(err);
}
return done(null, false, { msg: 'You have exceeded the maximum number of login attempts. Your account is locked until ' + moment(user.lockUntil).tz(config.server.timezone).format('LT z') + '. You may attempt to log in again after that time.' });
});
}
if (!user.isVerified) {
return done(null, false, { msg: 'Your email has not been verified. Check your inbox for a verification email.<p><i class="material-icons left">email</i>Re-send verification email</p>' });
}
user.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
}
else {
user.incrementLoginAttempts(function(err) {
if (err) {
return done(err);
}
return done(null, false, { msg: 'Invalid password. Please try again.' });
});
}
});
});
}));

Have a look on this: https://github.com/AdamPflug/express-brute
A brute-force protection middleware for express routes that rate-limits incoming requests, increasing the delay with each request in a fibonacci-like sequence.

I myself wondered how to tackle this, but I tried the following and I am not sure how good is it in terms of performance and good code.
Basically, I created a flag in my Schema called "login attempts" and set it to 0
Then in the login process, I do the following: compare the password, if it's okay then I log in. Else, I increment the login attempt flag in my DB each time the user enters the wrong password. If the login attempts exceed 3, I display an error message saying that you exceeded login attempts.
Now up to this point everything works, the next part is pretty much way of switching that flag to zero.
Now I used setTimeout function to run after 5 mins and switch that flag to 0 and it worked.
My main concern: Is it safe to use setTimeout like this.
the other concern is how is this going to affect the performance.
So in terms of getting the job done, it's working but in terms of performance and best method, I am not sure about that.

Related

In Mongo, how to change field value after certain amount of time has passed

Versions of this question have been asked for many databases but I did not see a version of this question on stackoverflow for MongoDB. We have a users collection in our database, and we are trying to create trial accounts for our website. We have the following user schema:
{
name: 'Joe Smith',
email: 'joesmith#gmail.com',
userTier: { value: 0, label: 'none' }
}
When a user signs up, their userTier starts at { value: 0, label: 'none' }, when they verify their email address, userTier goes to { value: 1, label: 'verified' } via a route in our Node API:
router.get('/verify-email/:userId/:token', async (req, res) => {
try {
const { userId, token } = req.params;
const emailToken = await EmailVerificationToken.findOne({ token: token });
if (!emailToken) { // bad token, do not verify
return res.redirect(`${frontEndUrl}/verify-email-page/no-token`);
}
const thisUser = await User.findOne({ _id: userId });
if (!thisUser) { // no user found, do not verify
return res.redirect(`${frontEndUrl}/verify-email-page/no-user`);
}
if (thisUser.tier.value > 0) { // if already verified
return res.redirect(`${frontEndUrl}/verify-email-page/already-verified`);
}
// otherwise, verify user
thisUser.tier = { value: 1, label: 'verified' };
thisUser.save(function (err) {
if (err) {
return res.redirect(`${frontEndUrl}/verify-email-page/cant-save-user`);
}
res.redirect(`${frontEndUrl}/verify-email-page/success`);
});
} catch (error) {
res.status(500).json({ statusCode: 500, message: error.message });
}
});
...not a perfect route with the different res.redirect()s, but it gets the job done. We are now looking for a way to temporarily update the userTier to { value: 2, label: 'trial' } for 1-2 weeks, after which the value automatically changes back to { value: 1, label: 'verified' }. We would prefer a mongo-only approach (perhaps we can set an expiration field in the user schema that automatically changes the userTier value when the expiration time is reached), although it seems unlikely that this is possible... How are 1 week trials typically coded up under the hood when using mongo + node?
I do not think there is a MongoDB-only way. I suggest that you set a Node.js cron function that would execute every night/morning and would deactivate the expired trials.
const schedule = require('node-schedule');
const job = schedule.scheduleJob('0 1 * * *', function(){
// update the users for whom the trial expired
});
MongoDB in the end is powered by a server that may crash or would restart when the machine restarts anyway. As far I know, there is no such functionality as memorising particular actions in the database server itself.
Instead, you can have a middleware to check if the trial expired.

Proper way to add a friend route in node.js and mongoose?

I'm planning to to create a route where a user could add another user as his/her friend, so that they could chat to each other once they are friends.
So basically once User A has sent a request to User B, User B will get a live notification about the request via socket.io
The problem right now is that, I couldn't come up with my own solution on how to implement the above scenario, from what I know, I should create two routes GET and POST
I'm using mongoose for database query, insert , update and delete
Here's my code
// GET route for getting the user's information -- Simple route
router.get('/users/:facebook_name', function(req, res) {
User.findOne(facebook_name, function(err, user) {
if (!user) {
res.json({message: "Couldn't find a user by that name"});
return;
}
res.json(user);
});
});
// POST route for adding a friend
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
// What should i put in this route to make the adding a friend feature works?
User.findOneAndUpdate(facebook_name, function(err, user) {
if (user) {
res.json({message: "You already send a friend request to that person"});
return;
}
// Send a live notification to the other user
socket.emit('sending request', {message: "added you as a friend"});
});
});
user Schema code -- Not really sure about this one either
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
friends: [{ type: Schema.Types.ObjectId, ref: 'User'}],
facebook: {
id: String,
token: String,
// email: String,
displayName: String,
photo: String
}
});
// Should I even create this schema?
var FriendsRequest = new Schema({
madeBy: [{ type: Schema.Types.ObjectId, ref: 'User'}],
})
module.exports = mongoose.model('User', UserSchema);
module.exports = mongoose.model('FriendsRequest', FriendsRequest);
I'm not entirely honest with you guys, in the POST route, i have no freaking idea on how to write the logic, because I'm really confuse right now, how the User B gonna get the live request notification? Should i create another route for that?
This is my problem when it comes to building slightly complex apps , i just couldn't come up with a good logic on how to do a certain feature even though it looks pretty easy. I've been stuck in this problem for almost 4 hours, browsing and reading the net, but I believe SO is the only place for me to find a clue on how to do something.
Thank you.
What you can do is create socket for each facebookName(if unique).
On Client Side:
socket.on('connection', function (data) {
socket.emit('setFacebookName', facebookName); });
}
Server saves each socket with facebookName:
socket.on('setFacebookName', function (facebookName) {
users[facebookName]=socket;
});
Now, when user sends chat request to that user in this request
// POST route for adding a friend
router.post('/friendships/create/:facebook_name', ensureAuthenticated, function(req, res) {
// What should i put in this route to make the adding a friend feature works?
User.findOneAndUpdate(facebook_name, function(err, user) {
if (user) {
res.json({message: "You already send a friend request to that person"});
return;
}
// Send a live notification to the other user
sendLiveNotification(facebook_name);
});
});
function sendLiveNotification(facebookName){
socket.on('send notification', function (facebookName) {
users[facebookName].emit('sending request', "has sent friend request");
});
}
You're trying to get a two step process, so you will need at least two calls where one is a request from the requester, and the other is the decision whether or not to allow that request from the requestee. You can handle your callback for the first function utilizing a Boolean where if it's a new request the user could be prompted with a popup on the client.
A good purpose of Mongoose is the extensions to the Schema that you can make, so here I'm adding two functions: one from the requester requesting requestee's friendship, and the other the decision of the requestee
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
friendsAccepted: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsRequested: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsPending: [{ type: Schema.Types.ObjectId, ref: 'User'}],
friendsRejected: [{ type: Schema.Types.ObjectId, ref: 'User'}],
facebook: {
id: String,
token: String,
// email: String,
displayName: String,
photo: String
}
});
UserSchema.statics.requesterInitiatedRequestForFriendship = function(requesterID, requesteeID, cb) {
mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
if (err) return cb(err);
mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
if (err) return cb(err);
if (requestee.friendsAccepted(requesterID) === -1 &&
requestee.friendsRequested(requesterID) === -1 &&
requestee.friendsPending(requesterID) === -1 &&
requestee.friendsRejected(requesterID) === -1) {
requestee.friendsPending.push(requesterID);
requester.friendsRequested.push(requesterID);
requestee.save();
requester.save();
cb(null, true);
} else {
cb(null, false);
};
});
});
};
UserSchema.statics.requesteeDecidedOnFriendship = function(requesterID, requesteeID, allowed, cb) {
mongoose.model('UserSchema').findOne({_id: requesterID}).exec(function(err, requester) {
if (err) return cb(err);
mongoose.model('UserSchema').findOne({_id: requesteeID}).exec(function(err, requestee) {
if (err) return cb(err);
if ((requestee.friendsAccepted(requesterID) === -1 &&
requestee.friendsRequested(requesterID) === -1 &&
requestee.friendsPending(requesterID) > -1 &&
requestee.friendsRejected(requesterID) === -1) &&
requester.friendsRequested(requesteeID) > -1) {
requestee.friendsPending.forEach(function(uid, idx) {
if (uid === requesterID) {
requestee.friendsPending.splice(idx, 1);
return;
};
});
requester.friendsRequested.forEach(function(uid, idx) {
if (uid === requesteeID) {
requester.friendsRequested.splice(idx, 1);
return;
};
});
if (allowed) {
requestee.friendsAccepted.push(requesterID);
requester.friendsAccepted.push(requesteeID);
} else {
requestee.friendsRejected.push(requesterID);
requester.friendsRejected.push(requesteeID);
}
requestee.save();
requester.save();
};
cb(null);
});
});
}
module.exports = mongoose.model('User', UserSchema);
So a couple things happening:
hasn't been tested
it's not DRY
it's limited without an additional Friendship Schema
With a Friendship Schema, you can define levels of rejection (e.g. "not at this time", etc.), you can more well flush out details and granular control for the changing behavior of friendships. In the above, you can see that once you're rejected, it's pretty fatalistic in that it's determined at no time you shall become friends! So to get more of that type of behavior, I'd definitely go with a Friendship Schema with it's statics and methods flushed out, as should be users.

Mongoose validation not working properly in mocha test

I am building a REST api with nodejs, using mongoose and mochajs to run some tests. I have the following scheme:
var subscriptionTypeSchema = new mongoose.Schema({
typeId : { type: Number, required: true, unique: true },
name : { type: String, required: true},
active : { type: Boolean, required: true }
});
Express route:
app.post('/1.0/subscriptiontype', subscriptiontype.create);
Controller:
exports.create = function(req, res) {
validation.subscriptionTypeValidator(req);
var errors = req.validationErrors();
if (errors) {
res.status(400).json({
errors: errors
});
} else {
var subscriptionType = new SubscriptionType();
subscriptionType.typeId = parseInt(req.body.typeId);
subscriptionType.name = req.body.name;
subscriptionType.active = req.body.active;
subscriptionType.save(function(err) {
if (err) {
var parsedError = mongooseutility.parseMongooseError(err);
res.status(400).json({
errors: [parsedError]
});
} else {
res.json({identifier: subscriptionType._id});
}
});
}
};
The mongoose utility maps the error codes to a more API friendly output (error codes 11001 and 11000 are mapped to a 'duplicate' error, as can be seen in the test).
Mocha before method:
before(function(done) {
db.connection.on('open', function() {
db.connection.db.dropDatabase(function(err) {
done();
});
});
});
I've verified that the database is dropped successfully.
The test itself makes a request using supertest. Before this test, I have a test that creates a subscription type with typeId 4 successfully, so this one should fail:
it('Should not create subscription with taken type id', function (done) {
request(app.privateapi)
.post('/1.0/subscriptiontype')
.set('Authorization', authorizationHeader)
.send({
typeId: 4,
name: 'New package',
active: 1
})
.expect(function (res) {
if (res.status !== 400) {
throw new Error('Status code was not 400');
}
var expectedResponse = { errors: [ { param: 'typeId', msg: 'duplicate' } ] };
if (JSON.stringify(res.body) !== JSON.stringify(expectedResponse)) {
throw new Error('Output was not was as expected');
}
})
.end(done);
});
Tests are invoked using grunt-simple-mocha.
This test works the first time, however when I run it a 2nd time it fails on the unique validation. A third time it works again. I've done some searching and found that it probably has something to do with a race condition while recreating indexes, so I've tried restarting mongodb before running the tests again, but that doesn't work. I've found a solution here: http://grokbase.com/t/gg/mongoose-orm/138qe75dvr/mongoose-unique-index-test-fail but I am not sure how to implement this. Any ideas?
Edit: for now I fixed it by dropping the database in an 'after' method (instead of 'before'). All the tests run fine, but it would be nice to keep the test data after the tests are done, for inspection etc...
You are not testing the creation of your tables so you can just empty your collections instead of creating the db.
Something along those lines (not tested):
beforeEach(function(done){
var models = Object.keys(mongoose.models);
var expects = models.length;
if(expects == 0) return done();
var removeCount = 1;
//maybe use async or something else but whatever
models.forEach(function(model){
model.remove({}, function(){
if(removeCount == expects){
done();
}
removeCount++;
})
});
});

Mongoose helper method has no findOne method?

How can I add helper methods to find and save objects in Mongoose. A friend told me to use helper methods but I cannot get them to work after a day. I always receive errors saying that either findOne() or save() does not exist OR that next callback is undefined (when node compiles ... before I execute it):
I've tried _schema.methods, _schema.statics... nothing works...
var email = require('email-addresses'),
mongoose = require('mongoose'),
strings = require('../../utilities/common/strings'),
uuid = require('node-uuid'),
validator = require('validator');
var _schema = new mongoose.Schema({
_id: {
type: String,
trim: true,
lowercase: true,
default: uuid.v4
},
n: { // Name
type: String,
required: true,
trim: true,
lowercase: true,
unique: true,
index: true
}
});
//_schema.index({
// d: 1,
// n: 1
//}, { unique: true });
_schema.pre('save', function (next) {
if (!this.n || strings.isNullOrWhitespace(this.n)){
self.invalidate('n', 'Domain name required but not supplied');
return next(new Error('Domain name required but not supplied'));
}
var a = email.parseOneAddress('test#' + this.n);
if (!a || !a.local || !a.domain){
self.invalidate('n', 'Name is not valid domain name');
return next(new Error('Name is not valid domain name'));
}
next();
});
_schema.statics.validateForSave = function (next) {
if (!this.n || strings.isNullOrWhitespace(this.n)){
return next(new Error('Domain name required but not supplied'));
}
var a = email.parseOneAddress('test#' + this.n);
if (!a || !a.local || !a.domain){
return next(new Error('Name is not valid domain name'));
}
next();
}
_schema.statics.findUnique = function (next) {
this.validateForSave(function(err){
if (err){ return next(err); }
mongoose.model('Domain').findOne({ n: this.n }, next);
//this.findOne({ n: this.n }, next);
});
}
_schema.statics.init = function (next) {
this.findUnique(function(err){
if (err){ return next(err); }
this.save(next);
});
}
var _model = mongoose.model('Domain', _schema);
module.exports = _model;
I believe you are running into issues because of your usage with this. Every time you enter a new function this's context is changing. You can read more about this at mdn.
Additionally your callbacks aren't allowing anything to be passed into the mongoose method. For example if I was to create the most basic "save" method I I would do the following:
_schema.statics.basicCreate = function(newDocData, next) {
new _model(newDocData).save(next);
}
Now if I wanted to search the Domain collection for a unique document I would use the following:
_schema.statics.basicSearch = function(uniqueName, next) {
var query = {n: uniqueName};
_model.findOne(query, function(err, myUniqueDoc) {
if (err) return next(err);
if (!myUniqueDoc) return next(new Error("No Domain with " + uniqueName + " found"));
next(null, myNewDoc);
});
}
Mongoose has built in validations for what you are doing:
_schema.path("n").validate(function(name) {
return name.length;
}, "Domain name is required");
_schema.path("n").validate(function(name) {
var a = email.parseOneAddress("test#" + name);
if (!a || !a.local || !a.domain) {
return false;
}
return true;
}, "Name is not a valid domain name");
It returns a boolean. If false, it passes an error to the .save() callback with the stated message. For validating uniqueness:
_schema.path("n").validate(function(name, next) {
var self = this;
this.model("Domain").findOne({n: name}, function(err, domain) {
if (err) return next(err);
if (domain) {
if (self._id === domain._id) {
return next(true);
}
return next(false);
}
return next(true);
});
}, "This domain is already taken");
You're using self = this here so that you can access the document inside the findOne() callback. false is being passed to the callback if the name exists and the result that is found isn't the document itself.
I've tried _schema.methods, _schema.statics
To clarify, .statics operate on the Model, .methods operate on the document. Zane gave a good example of statics, so here is an example of methods:
_schema.methods.isDotCom = function() {
return (/.com/).test(this.n);
}
var org = new Domain({n: "stuff.org"});
var com = new Domain({n: "things.com"});
org.isDotCom(); // false
com.isDotCom(); // true
Opinion: It's neat to have mongoose do validations, but it's very easy to forget it's happening. You also may want to have some validation in one area of your app while using different validations elsewhere. I'd avoid using most of it unless you definitively know you will have to do the same thing every time and will never NOT have to do it.
Methods/statics are a different story. It's very convenient to call isDotCom() instead of writing out a regex test every time you need to check. It performs a single and simple task that saves you some typing and makes your code more readable. Using methods for boolean checks can add a ton of readability. Defining statics like findByName (Zane's basicSearch) is great when you know you're going to do a simple query like that repeatedly.
Treat Mongoose as a utility, not as core functionality.

Mongoose unique validation error type

I'm using this schema with mongoose 3.0.3 from npm:
var schema = new Schema({
_id: Schema.ObjectId,
email: {type: String, required: true, unique: true}
});
If I try to save a email that is already in db, I expect to get a ValidationError like if a required field is omitted. However this is not the case, I get a MongoError: E11000 duplicate key error index.
Which is not a validation error (happens even if I remove the unique:true).
Any idea why?
I prefer putting it in path validation mechanisms, like
UserSchema.path('email').validate(function(value, done) {
this.model('User').count({ email: value }, function(err, count) {
if (err) {
return done(err);
}
// If `count` is greater than zero, "invalidate"
done(!count);
});
}, 'Email already exists');
Then it'll just get wrapped into ValidationError and will return as first argument when you call validate or save .
I had some issues with the approved answer. Namely:
this.model('User') didn't work for me.
the callback done wasn't working properly.
I resolved those issues by:
UserSchema.path('email').validate(async (value) => {
const emailCount = await mongoose.models.User.countDocuments({email: value });
return !emailCount;
}, 'Email already exists');
I use async/await which is a personal preference because it is much neater: https://javascript.info/async-await.
Let me know if I got something wrong.
This is expected behavior
The unique: true is equivalent to setting an index in mongodb like this:
db.myCollection.ensureIndex( { "email": 1 }, { unique: true } )
To do this type of validation using Mongoose (Mongoose calls this complex validation- ie- you are not just asserting the value is a number for example), you will need to wire in to the pre-save event:
mySchema.pre("save",function(next, done) {
var self = this;
mongoose.models["User"].findOne({email : self.email},function(err, results) {
if(err) {
done(err);
} else if(results) { //there was a result found, so the email address exists
self.invalidate("email","email must be unique");
done(new Error("email must be unique"));
} else {
done();
}
});
next();
});
Simply response to json
try {
let end_study_year = new EndStudyYear(req.body);
await end_study_year.save();
res.json({
status: true,
message: 'បានរក្សាទុក!'
})
}catch (e) {
res.json({
status: false,
message: e.message.toString().includes('duplicate') ? 'ទិន្នន័យមានរួចហើយ' : e.message.split(':')[0] // check if duplicate message exist
})
}
Sorry for answering an old question. After testing I feel good to have find these answers, so I will give my experience. Both top answers are great and right, just remember that:
if your document is new, you can just validate if count is higher than 0, thats the common situation;
if your document is NOT new and has modified the unique field, you need to validate with 0 too;
if your document is NOT new and has NOT being modified, just go ahead;
Here is what I made in my code:
UserSchema.path('email').validate(async function validateDuplicatedEmail(value) {
if (!this.isNew && !this.isModified('email')) return true;
try {
const User = mongoose.model("User");
const count = await User.countDocuments({ email: value });
if (count > 0) return false;
return true;
}
catch (error) {
return false;
}
}, "Email already exists");

Resources