简体   繁体   中英

can't hash a password and set it to field in mongodb schema

I'm experimenting with a demo blog platform by madhusudhan srinivasa for node.js, express, and mongodb. . I've created a blog heavily based on his implementation. But, I'm having trouble with hashing a password and creating a user. I've attached the model and controller for creating a user since I don't know exactly where the problem is.

My user creation form has user and password fields which are passed into the controller with bodyParser.

My problem is that when i submit the form to create a user, I get an undefined error from the hash validation function, "cannot get length of undefined."

If i comment this hash validation function out, a user is created. But when i look at the created user in mongodb command line, it doesn't have a hash field at all, but the name and salt fields are set correctly. However, when I console.log the hash inside encryptPassword(), it seems to output a correctly hashed password.

I have been working on this for hours and am completely at a loss to what the problem could be.

model:

var mongoose = require('mongoose')
    , Schema = mongoose.Schema
    , crypto = require('crypto')
    , _ = require('underscore')

// user schema

var UserSchema = new Schema ({
    name: { type: String, default: '' },
    hash: { type: String, default: '' },         
    salt: { type: String, default: '' }
})

UserSchema
    .virtual('password')
    .set(function(password) {
        this._password = password
        this.salt = this.makeSalt()
        this.hash = this.encryptPassword(password)
    })
    .get(function() { return this._password })


var validatePresenceOf = function (value) {   
    return value && value.length
}

UserSchema.path('name').validate(function(name) {
    return name.length
}, 'you need a name..')

UserSchema.path('name').validate(function(name, cb) {
    var User = mongoose.model('User')

    if (this.isNew || this.isModified('name')) {
    User.find({ name : name }).exec(function(err, users) {
        cb(!err && users.length === 0)
    })
    } else cb(true)
    }, 'name already exists..')

// i get an undefined error at the below function:

UserSchema.path('hash').validate(function(hash) {
    return hash.length
}, 'you need a password...')

UserSchema.pre('save', function(next) {
    if (!this.isNew) return next()

    if (!validatePresenceOf(this.password)) {
    next(new Error('invalid password.'))
    } else {
        next()
    }
})

UserSchema.methods = {

    // auth
    authenticate: function(plaintext) {
        return this.encryptPassword(plaintext) === this.hash 
    },

    // salt 
    makeSalt: function() {
        return crypto.randomBytes(128)
    },

    encryptPassword: function (password) {
        if (!password) return ''      
        crypto.pbkdf2(password, this.salt, 2000, 128, function(err, derivedKey) {
            if (err) throw err
            var myhash = derivedKey.toString()
            console.log('hash: ' + myhash)
            return myhash
        })
    } 
}

mongoose.model('User', UserSchema)

controller:

exports.create = function(req, res) {
    var user = new User(req.body)
    user.save(function (err) {
        if (err) {
            return res.render('signup', {
                errors: err.errors,
                user: user,
                title: 'SIGN UP'
            })
        }
        req.logIn(user, function(err) {
            if (err) return next(err)
            return res.redirect('backend')
        })
    })
}

Your encryption procedure uses crypto.pbkdf2 , which is an asynchronous function. This means encryptPassword() will not return your hash when your virtual setter is called. Your hash only gets returned inside of the callback passed to crypto.pbkdf2 - which is why console.log works in your example.

One way to solve this problem is to change encryptPassword() to make use of pbkdf2's synchronous sibling - crypto.pbkdf2Sync . Link to docs

Example below (with some error handling):

encryptPassword: function (password) {
    if (!password) return ''      
    var encrypted
    try {
        encrypted = crypto.pbkdf2Sync(password, this.salt, 2000, 128).toString();    
        return encrypted
    } catch (err) {
        // Handle error
    }  
} 
  encryptPassword: function (password) {
        if (!password || !this.salt)
            return '';
        var salt = new Buffer(this.salt, 'base64');
        return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM