Mongoose create() resolved before async middleware is finished - node.js

var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
email: {
type: string,
unique: true,
required: true,
trim: true
},
password: {
type: string,
required: true
},
authtokens: {
type: [{ type: mongoose.Schema.Types.ObjectId, ref: 'AuthToken' }]
}
});
//hashing a password before saving it to the database
UserSchema.pre('save', function (next) {
if (this.isNew) {
bcrypt.gensalt(10, function(err, salt) {
if (err) return next(err);
bcrypt.hash(this.password, salt, null, function (err, hash){
if (err) return next(err);
this.password = hash;
console.log('user.password ', this.password);
next();
});
});
} else next();
});
I call this from a controller:
'use strict';
var mongoose = require('mongoose'),
User = mongoose.model('User'),
AuthToken = mongoose.model('AuthToken');
exports.createUser = function(req, res, next) {
if (req.body.email && req.body.password && req.body.passwordConf) {
var userData = {
email: req.body.email,
password: req.body.password,
passwordConf: req.body.passwordConf
};
//use schema.create to insert data into the db
User.create(userData, function (err, user) {
console.log('user created ', user.password);
if (err) {
return next(err);
} else {
return res.redirect('/profile');
}
});
} else {
var err = new Error("Missing parameters");
err.status = 400;
next(err);
}
};
When a createUser is called with email user#email.com, password password, I get the output:
user.password $2a$10$wO.6TPUm5b1j6lvHdCi/JOTeEXHWhYernWU.ZzA3hfYhyWoOeugcq
user created password
Also, looking directly in the database, I see this user with plain text password -> password.
Why is user having plaintext password in the database. How can I store the hash instead?

In short, you forgot you were going into a callback which has a different functional scope and you're still referring to this, which is at that time not actually the "model" instance.
To correct this, take a copy of this before you do anything like launching another function with a callback:
UserSchema.pre('save', function(next) {
var user = this; // keep a copy
if (this.isNew) {
bcrypt.genSalt(10, function(err,salt) {
if (err) next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) next(err);
user.password = hash;
next();
});
});
}
});
An alternate approach of course is to modernize things and use Promise results with async/await. The bcrypt library which is actually the "core" and not a fork does this right out of the box:
UserSchema.pre('save', async function() {
if (this.isNew) {
let salt = await bcrypt.genSalt(10);
let hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
});
Aside from the modern approach being generally cleaner code, you also don't need to change the scope of this since we don't "dive in" to another function call. Everything gets changed in the same scope, and of course awaits the async calls before continuing.
Full Example - Callback
const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
const uri = 'mongodb://localhost/crypto';
var userSchema = new Schema({
email: String,
password: String
});
userSchema.pre('save', function(next) {
var user = this; // keep a copy
if (this.isNew) {
bcrypt.genSalt(10, function(err,salt) {
if (err) next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) next(err);
user.password = hash;
next();
});
});
}
});
const log = data => console.log(JSON.stringify(data, undefined, 2));
const User = mongoose.model('User', userSchema);
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await User.create({ email: 'ted#example.com', password: 'password' });
let result = await User.findOne();
log(result);
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Full Example - Promise async/await
const { Schema } = mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const uri = 'mongodb://localhost/crypto';
var userSchema = new Schema({
email: String,
password: String
});
userSchema.pre('save', async function() {
if (this.isNew) {
let salt = await bcrypt.genSalt(10);
let hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
});
const log = data => console.log(JSON.stringify(data, undefined, 2));
const User = mongoose.model('User', userSchema);
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await User.create({ email: 'ted#example.com', password: 'password' });
let result = await User.findOne();
log(result);
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Both show the password correctly encrypted, since we actually set the value in the model instance:
{
"_id": "5aec65f4853eed12050db4d9",
"email": "ted#example.com",
"password": "$2b$10$qAovc0m0VtmtpLg7CRZmcOXPDNi.2WbPjSFkfxSUqh8Pu5lyN4p7G",
"__v": 0
}

Related

Router doesn't get response from database functions

when running the app and testing the /signup route the data gets written to the db and i can console.log it from the database/models.js file, but in the routes/index.js file it returns "Something went wrong." and postman shows nothing, not even an empty array or object.
routes/index.js
var express = require('express');
var router = express.Router();
const database = require('../database/models');
router.post('/signup', function(req, res, next) {
if (!req.body.email || !isValidEmail(req.body.email))
res.status(400).send('Email invalid.');
else if (!req.body.username || !isValidCredential(req.body.username) || !req.body.password || !isValidCredential(req.body.password))
res.status(400).send('Username/password invalid.');
else {
const result = database.createUser(req.body.email, req.body.username, req.body.password);
if (result)
res.status(200).send(result);
else
res.send('Something went wrong.');
}
});
function isValidEmail (email) {
if (email.match("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"))
return true;
else
return false;
}
function isValidCredential (credential) {
if (credential.length < 6)
return false;
else if (credential.match(/^[a-z0-9]+$/i))
return true;
else
return false;
}
module.exports = router;
database/models.js
const tools = require('./tools');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
email: String,
username: String,
hashedPassword: String,
salt: String,
accessToken: { type: String, default: "" }
});
const User = mongoose.model('User', userSchema);
function createUser(email, username, password) {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
newUser.save(function (error, result) {
if (error)
return handleError(error);
return { email: result.email, username: result.username };
});
}
module.exports.createUser = createUser;
In your code you are not returning anything when calling the createUser function. Here a couple of considerations:
// index.js
const result = database.createUser(req.body.email, req.body.username, req.body.password);
since the createUser is an operation performed on a database, it will be probably asynchronous, and therefore also its result. I suggest the usage of async/await to be sure of the returned result. Also, you need to change the code of your models.js file to return a Promise and await for it.
function createUser(email, username, password) {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
return new Promise((resolve, reject)=> {
newUser.save(function (error, result) {
if (error) reject(error);
resolve({ email: result.email, username: result.username });
});
});
}
and than you will have to await for your result. You can do it in the following way:
// index.js
// Add async here
router.post('/signup', async function(req, res, next) {
// ...other code
// Add await here
const result = await database.createUser(req.body.email, req.body.username, req.body.password);
I figured it out.
in routes/index.js use async/await like this:
router.post('/signup', async function(req, res, next) {
try {
if (!req.body.email || !isValidEmail(req.body.email))
res.status(400).send('Email invalid.');
else if (!req.body.username || !isValidCredential(req.body.username) || !req.body.password || !isValidCredential(req.body.password))
res.status(400).send('Username/password invalid.');
else {
const result = await database.createUser(req.body.email, req.body.username, req.body.password);
if (result)
res.status(200).send(result);
else
res.status(403).send(result);
}
} catch (error) { return error; }
});
and in database/models.js use async/await as well, but also rewrite mongoose methods into ones without callbacks, with returns into variables, like this:
async function createUser(email, username, password) {
try {
const hashPass = tools.generatePassword(password);
const newUser = new User({
email: email,
username: username,
hashedPassword: hashPass.hash,
salt: hashPass.salt
});
const result = await newUser.save();
return { email: result.email, username: result.username };
} catch (error) { console.log (error); return error; }
}

Change bcrypt.compareSync to async if better

Here my authentication process:
Session > Controller.js
const jwt = require("jsonwebtoken");
const repository = require("./repository");
const config = require("../../config");
const logger = require("../../utilities/logger");
exports.login = async (req, res) => {
if (!req.body.password || !req.body.username) {
res.preconditionFailed("Credentials required");
return;
}
try {
const user = await repository.findUser(req.body);
if (!user || !user.comparePasswords(req.body.password)) {
res.json({ success: false, message: "Authentication failed." });
return;
}
const token = jwt.sign(user.toObject(), config.secret, { expiresIn: 1440 });
logger.info("User loged in with success. Login token", token);
res.json({
success: true,
token,
});
} catch (err) {
res.send(err);
}
};
User > Model.js
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const SALT_WORK_FACTOR = 10;
const Schema = mongoose.Schema;
const userSchema = new Schema({
id: { type: String, required: true },
username: { type: String, required: true },
password: { type: String, required: true },
}, {
timestamps: true,
});
userSchema.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
userSchema.methods.comparePasswords = function(candidatePassword) {
return bcrypt.compareSync(candidatePassword, this.password);
};
module.exports = mongoose.model("User", userSchema);
In the user model, I'm using the bcrypt method compareSync. It's recommended to use the async method compare (https://www.npmjs.com/package/bcrypt).
Could someone explain why? And what would be a good implementation? It's working well like that but I would like to be sure I'm using the best practices.
compareSync is extremely CPU intensive task. Since Node.js Event Loop is single threaded, you block entire application unless you comparison got finished.
Main rule using node.js is to always avoid synchronous code execution.
Reason is event-driven nature of framework.
Good & detailed explanation can be found here

500 internal error with authentication password in express

as a starting copypasteprogrammer I started to add some salting and hashing to my app. The creation of a new User works fine. But the authentication of a user/password in Postman gives me a hard time. I keep getting errors. Now a 500 internal service error. Who can give me a little advice?
Here the schema:
const userSchema = new Schema({
userName: { //email
type: String,
required: true,
unique: true,
trim: true
},
password: {
type: String,
required: true
}
});
// hashing and salting before saving
userSchema.pre('save', function (next) {
let user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
//generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
mongoose.model('User', userSchema);
Here the routes:
const express = require('express');
const router = express.Router();
const ctrlAuthFiles = require('../controllers/authFiles');
router
.route('/login')
.post(ctrlAuthFiles.userLogin);
module.exports = router;
Here the controller:
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
const User1 = mongoose.model('User');
//fetch user and test password verification
const userLogin = function (req, res) {
if (req.body) {
User1
.findOne({ userName: req.body.username })
.exec((err, user) => {
if (!user) {
res
.status(404)
.json({
"message": "username or password not found"
});
return;
} else if (err) {
res
.status(404)
.json(err);
return;
}
});
User1.comparePassword(req.body.password, function (err, isMatch) {
if (err) throw err;
console.log(isMatch);
});
User1.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err)
return cb(err);
cb(null, isMatch);
});
}
} else {
res
.status(404)
.json({
"message": "no username or password"
});
}
};

Mongoose inserts document without properties

MongoDB inserts document without properties(look at the bottom of the post to see how my collection looks like).I'm using postman to test my db when I try to insert the following:
{
"username":"JohnDoe",
"password" : "123456"
}
I'm building a MEAN stack application.Also, I noticed if I set the properties to be required, postman tells me that it Failed to register user. That's why I comment it out, but even without it and getting a postive response I still get empty documents in my collection.
Postman tells me:
{
"success": true,
"msg": "User registered"
}
My user.js file
const bcrypt = require ('bcryptjs');
const config = require ('../config/database');
//UserSchema
const UserSchema = mongoose.Schema({
username: {
type: String,
//required: true
},
password: {
type: String,
//required: true
}
});
const User = module.exports = mongoose.model("User", UserSchema);
//To user function outside
module.exports.getUserById = function(id, callback){
User.findById(id,callback);
}
module.exports.getUserByUsername= function(username, callback){
const query = {username: username}
User.findOne (query,callback);
}
User.addUser = function (newUser, callback) {
bcrypt.genSalt(10, (err, salt) =>{
bcrypt.hash(newUser.password, salt, (err, hash) => {
newUser.password = hash;
newUser.save(callback);
});
});
}
My users.js file:
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../modules/user');
// Register
router.post('/register', (req, res, next) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
if(err){
res.json({success: false, msg:'Failed to register user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
});
module.exports = router;
What I see in my collection:
{
"_id": {
"$oid": "5937b36bafdd733088cb27d0"
},
"__v": 0
}
You should learn about what is mongoose statics and methods.
In User model you should be declaring the functions as methods and statics based on the way you want it.
const bcrypt = require ('bcryptjs');
const config = require ('../config/database');
//UserSchema
const UserSchema = mongoose.Schema({
username: {
type: String,
//required: true
},
password: {
type: String,
//required: true
}
});
//To user function outside
UserSchema.statics.getUserById = function(id, callback){
User.findById(id,callback);
}
UserSchema.statics.getUserByUsername= function(username, callback){
const query = {username: username}
User.findOne (query,callback);
}
UserSchema.methods.addUser = function (callback) {
bcrypt.genSalt(10, (err, salt) =>{
bcrypt.hash(newUser.password, salt, (err, hash) => {
this.password = hash;
this.save(callback);
});
});
}
exports.User = mongoose.model("User", UserSchema);
In your controller user file, you should use addUser with your instance of the User model not on the Model you exporting. Check below..
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../modules/user');
// Register
router.post('/register', (req, res, next) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
newUser.addUser(function(err, user) => {
if(err){
res.json({success: false, msg:'Failed to register user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
});
module.exports = router;

Mongoose password hashing

I am looking for a good way to save an Account to MongoDB using mongoose.
My problem is: The password is hashed asynchronously. A setter wont work here because it only works synchronous.
I thought about 2 ways:
Create an instance of the model and save it in the callback of the
hash function.
Creating a pre hook on 'save'
Is there any good solution on this problem?
The mongodb blog has an excellent post detailing how to implement user authentication.
http://blog.mongodb.org/post/32866457221/password-authentication-with-mongoose-part-1
The following is copied directly from the link above:
User Model
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true }
});
UserSchema.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
Usage
var mongoose = require(mongoose),
User = require('./user-model');
var connStr = 'mongodb://localhost:27017/mongoose-bcrypt-test';
mongoose.connect(connStr, function(err) {
if (err) throw err;
console.log('Successfully connected to MongoDB');
});
// create a user a new user
var testUser = new User({
username: 'jmar777',
password: 'Password123'
});
// save the user to database
testUser.save(function(err) {
if (err) throw err;
});
// fetch the user and test password verification
User.findOne({ username: 'jmar777' }, function(err, user) {
if (err) throw err;
// test a matching password
user.comparePassword('Password123', function(err, isMatch) {
if (err) throw err;
console.log('Password123:', isMatch); // -> Password123: true
});
// test a failing password
user.comparePassword('123Password', function(err, isMatch) {
if (err) throw err;
console.log('123Password:', isMatch); // -> 123Password: false
});
});
For those who are willing to use ES6+ syntax can use this -
const bcrypt = require('bcryptjs');
const mongoose = require('mongoose');
const { isEmail } = require('validator');
const { Schema } = mongoose;
const SALT_WORK_FACTOR = 10;
const schema = new Schema({
email: {
type: String,
required: true,
validate: [isEmail, 'invalid email'],
createIndexes: { unique: true },
},
password: { type: String, required: true },
});
schema.pre('save', async function save(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
this.password = await bcrypt.hash(this.password, salt);
return next();
} catch (err) {
return next(err);
}
});
schema.methods.validatePassword = async function validatePassword(data) {
return bcrypt.compare(data, this.password);
};
const Model = mongoose.model('User', schema);
module.exports = Model;
TL;DR - Typescript solution
I have arrived here when I was looking for the same solution but using typescript. So for anyone interested in TS solution to the above problem, here is an example of what I ended up using.
imports && contants:
import mongoose, { Document, Schema, HookNextFunction } from 'mongoose';
import bcrypt from 'bcryptjs';
const HASH_ROUNDS = 10;
simple user interface and schema definition:
export interface IUser extends Document {
name: string;
email: string;
password: string;
validatePassword(password: string): boolean;
}
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
user schema pre-save hook implementation
userSchema.pre('save', async function (next: HookNextFunction) {
// here we need to retype 'this' because by default it is
// of type Document from which the 'IUser' interface is inheriting
// but the Document does not know about our password property
const thisObj = this as IUser;
if (!this.isModified('password')) {
return next();
}
try {
const salt = await bcrypt.genSalt(HASH_ROUNDS);
thisObj.password = await bcrypt.hash(thisObj.password, salt);
return next();
} catch (e) {
return next(e);
}
});
password validation method
userSchema.methods.validatePassword = async function (pass: string) {
return bcrypt.compare(pass, this.password);
};
and the default export
export default mongoose.model<IUser>('User', userSchema);
note: don't forget to install type packages (#types/mongoose, #types/bcryptjs)
I think this is a good way by user Mongoose and bcrypt!
User Model
/**
* Module dependences
*/
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt');
const SALT_WORK_FACTOR = 10;
// define User Schema
const UserSchema = new Schema({
username: {
type: String,
unique: true,
index: {
unique: true
}
},
hashed_password: {
type: String,
default: ''
}
});
// Virtuals
UserSchema
.virtual('password')
// set methods
.set(function (password) {
this._password = password;
});
UserSchema.pre("save", function (next) {
// store reference
const user = this;
if (user._password === undefined) {
return next();
}
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) console.log(err);
// hash the password using our new salt
bcrypt.hash(user._password, salt, function (err, hash) {
if (err) console.log(err);
user.hashed_password = hash;
next();
});
});
});
/**
* Methods
*/
UserSchema.methods = {
comparePassword: function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
}
module.exports = mongoose.model('User', UserSchema);
Usage
signup: (req, res) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
// save user
newUser.save((err, user) => {
if (err) throw err;
res.json(user);
});
}
Result
Result
The Mongoose official solution requires the model to be saved before using the verifyPass method, which can cause confusion. Would the following work for you? (I am using scrypt instead of bcrypt).
userSchema.virtual('pass').set(function(password) {
this._password = password;
});
userSchema.pre('save', function(next) {
if (this._password === undefined)
return next();
var pwBuf = new Buffer(this._password);
var params = scrypt.params(0.1);
scrypt.hash(pwBuf, params, function(err, hash) {
if (err)
return next(err);
this.pwHash = hash;
next();
});
});
userSchema.methods.verifyPass = function(password, cb) {
if (this._password !== undefined)
return cb(null, this._password === password);
var pwBuf = new Buffer(password);
scrypt.verify(this.pwHash, pwBuf, function(err, isMatch) {
return cb(null, !err && isMatch);
});
};
Another way to do this using virtuals and instance methods:
/**
* Virtuals
*/
schema.virtual('clean_password')
.set(function(clean_password) {
this._password = clean_password;
this.password = this.encryptPassword(clean_password);
})
.get(function() {
return this._password;
});
schema.methods = {
/**
* Authenticate - check if the passwords are the same
*
* #param {String} plainText
* #return {Boolean}
* #api public
*/
authenticate: function(plainPassword) {
return bcrypt.compareSync(plainPassword, this.password);
},
/**
* Encrypt password
*
* #param {String} password
* #return {String}
* #api public
*/
encryptPassword: function(password) {
if (!password)
return '';
return bcrypt.hashSync(password, 10);
}
};
Just save your model like, the virtual will do its job.
var user = {
username: "admin",
clean_password: "qwerty"
}
User.create(user, function(err,doc){});
const bcrypt = require('bcrypt');
const saltRounds = 5;
const salt = bcrypt.genSaltSync(saltRounds);
module.exports = (password) => {
return bcrypt.hashSync(password, salt);
}
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const hashPassword = require('../helpers/hashPassword')
const userSchema = new Schema({
name: String,
email: {
type: String,
match: [/^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, `Please fill valid email address`],
validate: {
validator: function() {
return new Promise((res, rej) =>{
User.findOne({email: this.email, _id: {$ne: this._id}})
.then(data => {
if(data) {
res(false)
} else {
res(true)
}
})
.catch(err => {
res(false)
})
})
}, message: 'Email Already Taken'
}
},
password: {
type: String,
required: [true, 'Password required']
}
});
userSchema.pre('save', function (next) {
if (this.password) {
this.password = hashPassword(this.password)
}
next()
})
const User = mongoose.model('User', userSchema)
module.exports = User
const mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
SALT_WORK_FACTOR = 10;
const userDataModal = mongoose.Schema({
username: {
type: String,
required : true,
unique:true
},
password: {
type: String,
required : true
}
});
userDataModal.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
userDataModal.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
// Users.index({ emaiId: "emaiId", fname : "fname", lname: "lname" });
const userDatamodal = module.exports = mongoose.model("usertemplates" , userDataModal)
//inserting document
userDataModel.findOne({ username: reqData.username }).then(doc => {
console.log(doc)
if (doc == null) {
let userDataMode = new userDataModel(reqData);
// userDataMode.password = userDataMode.generateHash(reqData.password);
userDataMode.save({new:true}).then(data=>{
let obj={
success:true,
message: "New user registered successfully",
data:data
}
resolve(obj)
}).catch(err=>{
reject(err)
})
}
else {
resolve({
success: true,
docExists: true,
message: "already user registered",
data: doc
}
)
}
}).catch(err => {
console.log(err)
reject(err)
})
//retriving and checking
// test a matching password
user.comparePassword(requestData.password, function(err, isMatch) {
if (err){
reject({
'status': 'Error',
'data': err
});
throw err;
} else {
if(isMatch){
resolve({
'status': true,
'data': user,
'loginStatus' : "successfully Login"
});
console.log('Password123:', isMatch); // -> Password123: true
}
I guess it would be better to use the hook, after some research i found
http://mongoosejs.com/docs/middleware.html
where it says:
Use Cases:
asynchronous defaults
I prefer this solution because i can encapsulate this and ensure that an account can only be saved with a password.
I used .find({email}) instead of .findOne({email}).
Make sure to use .findOne(...) to get a user.
Example:
const user = await <user>.findOne({ email });

Resources