Mongoose password hashing












30















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?










share|improve this question



























    30















    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?










    share|improve this question

























      30












      30








      30


      17






      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?










      share|improve this question














      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?







      node.js mongodb mongoose






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 29 '13 at 16:55









      pfriedpfried

      3,07112854




      3,07112854
























          6 Answers
          6






          active

          oldest

          votes


















          96














          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 user to database
          testUser.save(function(err) {
          if (err) throw err;
          });

          // fetch 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
          });
          });





          share|improve this answer





















          • 10





            Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

            – k00k
            Aug 6 '15 at 14:13






          • 2





            Correct. What you could do is a separate find() and then save() function?

            – Willem Mulder
            Nov 6 '15 at 7:24



















          3














          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






          share|improve this answer

































            2














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





            share|improve this answer































              0














              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.






              share|improve this answer































                0














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





                share|improve this answer































                  0














                  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;





                  share|improve this answer























                    Your Answer






                    StackExchange.ifUsing("editor", function () {
                    StackExchange.using("externalEditor", function () {
                    StackExchange.using("snippets", function () {
                    StackExchange.snippets.init();
                    });
                    });
                    }, "code-snippets");

                    StackExchange.ready(function() {
                    var channelOptions = {
                    tags: "".split(" "),
                    id: "1"
                    };
                    initTagRenderer("".split(" "), "".split(" "), channelOptions);

                    StackExchange.using("externalEditor", function() {
                    // Have to fire editor after snippets, if snippets enabled
                    if (StackExchange.settings.snippets.snippetsEnabled) {
                    StackExchange.using("snippets", function() {
                    createEditor();
                    });
                    }
                    else {
                    createEditor();
                    }
                    });

                    function createEditor() {
                    StackExchange.prepareEditor({
                    heartbeatType: 'answer',
                    autoActivateHeartbeat: false,
                    convertImagesToLinks: true,
                    noModals: true,
                    showLowRepImageUploadWarning: true,
                    reputationToPostImages: 10,
                    bindNavPrevention: true,
                    postfix: "",
                    imageUploader: {
                    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                    allowUrls: true
                    },
                    onDemand: true,
                    discardSelector: ".discard-answer"
                    ,immediatelyShowMarkdownHelp:true
                    });


                    }
                    });














                    draft saved

                    draft discarded


















                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f14588032%2fmongoose-password-hashing%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown

























                    6 Answers
                    6






                    active

                    oldest

                    votes








                    6 Answers
                    6






                    active

                    oldest

                    votes









                    active

                    oldest

                    votes






                    active

                    oldest

                    votes









                    96














                    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 user to database
                    testUser.save(function(err) {
                    if (err) throw err;
                    });

                    // fetch 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
                    });
                    });





                    share|improve this answer





















                    • 10





                      Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                      – k00k
                      Aug 6 '15 at 14:13






                    • 2





                      Correct. What you could do is a separate find() and then save() function?

                      – Willem Mulder
                      Nov 6 '15 at 7:24
















                    96














                    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 user to database
                    testUser.save(function(err) {
                    if (err) throw err;
                    });

                    // fetch 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
                    });
                    });





                    share|improve this answer





















                    • 10





                      Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                      – k00k
                      Aug 6 '15 at 14:13






                    • 2





                      Correct. What you could do is a separate find() and then save() function?

                      – Willem Mulder
                      Nov 6 '15 at 7:24














                    96












                    96








                    96







                    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 user to database
                    testUser.save(function(err) {
                    if (err) throw err;
                    });

                    // fetch 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
                    });
                    });





                    share|improve this answer















                    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 user to database
                    testUser.save(function(err) {
                    if (err) throw err;
                    });

                    // fetch 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
                    });
                    });






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Aug 18 '16 at 7:43









                    Gayan Hewa

                    1,12631334




                    1,12631334










                    answered Jan 30 '13 at 1:48









                    NoahNoah

                    26.7k13030




                    26.7k13030








                    • 10





                      Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                      – k00k
                      Aug 6 '15 at 14:13






                    • 2





                      Correct. What you could do is a separate find() and then save() function?

                      – Willem Mulder
                      Nov 6 '15 at 7:24














                    • 10





                      Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                      – k00k
                      Aug 6 '15 at 14:13






                    • 2





                      Correct. What you could do is a separate find() and then save() function?

                      – Willem Mulder
                      Nov 6 '15 at 7:24








                    10




                    10





                    Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                    – k00k
                    Aug 6 '15 at 14:13





                    Just a note for someone that might try and fail to do this with an 'update' (what I did initially), from the mongoose docs: Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc.

                    – k00k
                    Aug 6 '15 at 14:13




                    2




                    2





                    Correct. What you could do is a separate find() and then save() function?

                    – Willem Mulder
                    Nov 6 '15 at 7:24





                    Correct. What you could do is a separate find() and then save() function?

                    – Willem Mulder
                    Nov 6 '15 at 7:24













                    3














                    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






                    share|improve this answer






























                      3














                      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






                      share|improve this answer




























                        3












                        3








                        3







                        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






                        share|improve this answer















                        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







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Jan 31 '18 at 8:34

























                        answered Jan 31 '18 at 7:57









                        xincmmxincmm

                        312




                        312























                            2














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





                            share|improve this answer




























                              2














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





                              share|improve this answer


























                                2












                                2








                                2







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





                                share|improve this answer













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






                                share|improve this answer












                                share|improve this answer



                                share|improve this answer










                                answered Jun 11 '15 at 16:42









                                alex94puchadesalex94puchades

                                1038




                                1038























                                    0














                                    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.






                                    share|improve this answer




























                                      0














                                      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.






                                      share|improve this answer


























                                        0












                                        0








                                        0







                                        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.






                                        share|improve this answer













                                        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.







                                        share|improve this answer












                                        share|improve this answer



                                        share|improve this answer










                                        answered Jan 29 '13 at 18:40









                                        pfriedpfried

                                        3,07112854




                                        3,07112854























                                            0














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





                                            share|improve this answer




























                                              0














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





                                              share|improve this answer


























                                                0












                                                0








                                                0







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





                                                share|improve this answer













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






                                                share|improve this answer












                                                share|improve this answer



                                                share|improve this answer










                                                answered Feb 1 '16 at 22:07









                                                pkarcpkarc

                                                32936




                                                32936























                                                    0














                                                    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;





                                                    share|improve this answer




























                                                      0














                                                      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;





                                                      share|improve this answer


























                                                        0












                                                        0








                                                        0







                                                        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;





                                                        share|improve this answer













                                                        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;






                                                        share|improve this answer












                                                        share|improve this answer



                                                        share|improve this answer










                                                        answered Nov 22 '18 at 13:24









                                                        SohailSohail

                                                        2,28711628




                                                        2,28711628






























                                                            draft saved

                                                            draft discarded




















































                                                            Thanks for contributing an answer to Stack Overflow!


                                                            • Please be sure to answer the question. Provide details and share your research!

                                                            But avoid



                                                            • Asking for help, clarification, or responding to other answers.

                                                            • Making statements based on opinion; back them up with references or personal experience.


                                                            To learn more, see our tips on writing great answers.




                                                            draft saved


                                                            draft discarded














                                                            StackExchange.ready(
                                                            function () {
                                                            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f14588032%2fmongoose-password-hashing%23new-answer', 'question_page');
                                                            }
                                                            );

                                                            Post as a guest















                                                            Required, but never shown





















































                                                            Required, but never shown














                                                            Required, but never shown












                                                            Required, but never shown







                                                            Required, but never shown

































                                                            Required, but never shown














                                                            Required, but never shown












                                                            Required, but never shown







                                                            Required, but never shown







                                                            Popular posts from this blog

                                                            Create new schema in PostgreSQL using DBeaver

                                                            Deepest pit of an array with Javascript: test on Codility

                                                            Costa Masnaga