Reputation: 533
For hashing a password before the object is saved to mongodb, i use the built-in pre-save hook comming with mongoose. But what is the correct way to handle hashing during updates?
I tried to solve this with the pre-update hook, but this has some significant disadvantages, as it bypasses the model validation (e.g password length).
That was my approach (shortened):
userSchema.pre('findOneAndUpdate', function (next) {
let query = this;
let update = query.getUpdate();
if (!update.password) {
return next();
}
hashPassword(update.password, function (err, hash) {
//handle error or go on...
//e.g update.password = hash;
});
});
So, what is the preferred way to solve this problem?
Upvotes: 7
Views: 13386
Reputation: 1
The safer way
AccountSchema.pre('save', async function () {
if (this.password && this.isModified('password')) {
this.password = encrypt(this.password)
}
})
Upvotes: 0
Reputation: 3838
I would use a pre 'save'
middleware:
AccountSchema.pre('save', function(next) {
this._doc.password = encrypt(this._doc.password);
next();
});
But doing this requires to use save
also for updating a document:
Account.findById(id, function(err, doc) {
if (err) return false;
doc.password = "baloony2";
doc.save();
});
As stated in the documentation for update:
[...]
Although values are casted to their appropriate types when using update, the following are not applied:
- defaults
- setters
- validators
- middleware
If you need those features, use the traditional approach of first retrieving the document.
A verifiable example:
const crypto = require('crypto');
const algorithm = 'aes-256-ctr';
const password = 'aSjlkvS89';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.connect("mongodb://localhost:33023/test_1");
mongoose.Promise = require('bluebird');
function encrypt(text) {
var cipher = crypto.createCipher(algorithm, password);
var crypted = cipher.update(text, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
}
// SCHEMA -------------------------------------------------
var AccountSchema = new Schema({
name: {
type: String,
unique: true
},
password: String
});
var id;
AccountSchema.pre('save', function(next) {
id = this._doc._id;
var pwd = this._doc.password;
console.log("hashing password: " + pwd);
this._doc.password = encrypt(pwd);
next();
});
// MODEL --------------------------------------------------
var Account = mongoose.model('Account', AccountSchema);
// DATA ---------------------------------------------------
new Account({
name: "john",
password: "baloony1"
})
.save()
.then(function(res) {
Account.findById(id, function(err, doc) {
if (err) return false;
doc.password = "baloony2";
doc.save();
});
});
Extra info on the example:
Upvotes: 9