How to store crypto pbkdf2 in mongoDB? - node.js

I'm using the following function to hash a user's password, following Express's example for authentication:
function hash(pwd, salt, fn) {
// Bytesize
var len = 128,
// Iterations. ~300ms
iterations = 12000;
if (3 == arguments.length) {
crypto.pbkdf2(pwd, salt, iterations, len, fn);
} else {
fn = salt;
crypto.randomBytes(len, function(err, salt){
if (err) return fn(err);
salt = salt.toString('base64');
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
if (err) return fn(err);
fn(null, salt, hash);
});
});
}
}
The salt, as you can see, is returned as a string encoded in base64. The hash, however, is returned as a SlowBuffer. This same function is also used to compare hashes when trying to log a user in.
My Mongoose schema for Users specifies that the hash should be of type String. This ends up storing the hash in a strange way, resulting in contents like this which wreaked havoc on my mongo host:
My question is, is there a better/smarter way to store this hash in my database? I've tried encoding it with .toString('hex'), and I've also tried changing the hash type in the User schema to buffer, but both these approaches made all comparisons false when trying to log users in. Comparisons are made in my authenticate function, seen below:
function authenticate(name, pass, fn) {
var findUser = function(username) {
var deferred = Q.defer(),
populateObj = [
// list of paths to populate objects normally goes here
];
User.findOne({ username: name }).populate(populateObj).exec(function (err, retrievedUser) {
if (err || !retrievedUser) {
console.log(err);
deferred.reject('Cannot find user.');
}
else {
deferred.resolve(retrievedUser);
}
});
return deferred.promise;
};
findUser(name).then(function(data) {
// apply the same algorithm to the POSTed password, applying
// the hash against the pass / salt, if there is a match we
// found the user
hash(pass, data.salt, function(err, hash){
if (err) return fn(err);
if (hash == data.hash) return fn(null, data);
return fn('Invalid password.');
});
}, function() {
return fn('Failed to retrieve user.');
});
}

Storing the hash as a hex string in the database works okay for me (storing them 'raw' in either a String or a Buffer property doesn't):
var crypto = require('crypto');
var mongoose = require('mongoose');
var client = mongoose.connect('mongodb://localhost/test');
var UserSchema = new mongoose.Schema({
salt : String,
hash : String
});
var User = mongoose.model('User', UserSchema);
hash('secret', function(err, salt, key) {
new User({ salt : salt, hash : key.toString('hex') }).save(function(err, doc) {
User.findById(doc._id, function(err, doc) {
hash('secret', doc.salt, function(err, key) {
console.log('eq', doc.hash === key.toString('hex'));
});
});
});
});
(by the way, both crypto.pbkdf2 and crypto.randomBytes have synchronous counterparts)

Related

I want my pre('save') mongoose function to operate only once

I do not know if the exact request in title is possible, but if not; i would really appreciate an alternate solution.
I have this pre save method of mongoose
ownerSchema.pre("save", function(next) {
const owner = this;
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(owner.password, salt, function(err, hash) {
// Store hash in your password DB.
owner.password = hash;
next();
});
});
});
When i save new user(owner) a hash is created successfully and all is good>
The problem occurs when i login. when i login i generate jwt with a mongoose custom method as below
ownerSchema.methods.generateToken = function(cb) {
var owner = this;
var token = jwt.sign(
{
_id: owner._id,
username: owner.username,
email: owner.email,
category: owner.category === 0 ? false : true,
phones: owner.phones,
address: owner.address
},
config.SECRET,
{ expiresIn: "1h" }
);
owner.token= token;
owner.save(function(err,owner){
if(err) return cb(err);
cb(null,owner);
})
};
as you see i generate token to send it in "res" and at the same time i add the new token to the record in the data base. all working fine till now and the response is returned successfully>
BUT!! while i performed save() in the generate token function to save the token>> the previous pre(save) function ran again, so that a new hash is generated for the password feild.
when i try to login again, the password had already changed from calling the pre save hashing function when generating the token in the first login.
Any workaround for solving this issue?
You could use isModified method on your 'password' field.
I use it in this way, only run bcrypt if the password property was changed:
UserSchema.pre('save', function (next) {
var user = this;
if (user.isModified('password')) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
next();
});
});
} else {
next();
}
});

bcrypt always returns false

I am trying to store the hash password and checking the it is valid or not.
var bcrypt = require('bcrypt');
let data = "";
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("my password", salt, function(err, hash) {
// Store hash in your password DB.
console.log(hash);
data = hash;
});
});
bcrypt.compare("my password", data, function(err, res) {
// res === true
console.log(res)
});
return value always false.?
but if I move the compare inside genSalt function its returning true.
Thanks
You are dealing with asynchronous functions in node.js, so this result is expected. For the clearer understanding of the problem try to console.log data before bcrypt.compare. I can certainly say it would be equal to an initial value of "".
Then try to move your compare function inside the .hash callback
var bcrypt = require('bcrypt');
let data = "";
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("my password", salt, function(err, hash) {
// Store hash in your password DB.
console.log(hash);
data = hash;
console.log(data); // Here data equals to your hash
bcrypt.compare("my password", data, function(err, res) {
// res === true
console.log(res)
});
});
});
console.log('data') // Here data equals to initial value of ""
You can use async/await functions to make it look like synchronous code and get rid of callbacks. Luckily, bcrypt support promise interface that is used by async/await
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash("my password", salt);
const result = await bcrypt.compare("my password", data);
console.log(result);

Unable to use variable outside the function

I am using Koa module - Node js and mysql & stuck on a problem.
In signup function (inside welcomeCtrl.js) I have:
var bcrypt = require('bcrypt');
module.exports = {
signup: function* (next) {
bcrypt.genSalt(10, function(err, salt) {
console.log(salt);
bcrypt.hash(password, salt, function(err, hash) {
// Store hash in your password DB.
console.log(hash); //no error
var hashedPassword = hash;
});
});
console.log(bcrypt.hash.hash); //gives error
//or
console.log(bcrypt.hash.hahedPassword); //gives error
queryString = "insert into user(email,name, phone, password, course_id, dpt_id) values('%s','%s','%s','%s','%s','%s')";
query = util.format(queryString, email, name, phone, hash, courseId, dptId);
results = yield databaseUtils.executeQuery(query);
this.redirect('/');
}
}
I am calling signup post function in different routes.js file as:
router.post('/signup', welcomeCtrl.signup);
Here is the executeQuery function in the databaseUtils.js file
var executeQuery = function(query, callback) {
pool.getConnection(function(err, connection) {
// Use the connection
connection.query(query, function(err, rows, fields) {
connection.release();
if(err) {
err.mysqlQuery = query;
logger.logError(err);
}
if(typeof callback == "function") {
callback(err, rows);
}
// Don't use the connection here, it has been returned to the pool.
});
});
};
module.exports = {
executeQuery: thunkify(executeQuery),
executePlainQuery: executeQuery
};
Is there any way to use hash variable outside the function so that it can be inserted in query?
You should move your query function into the callback for bcrypt.hash.
bcrypt.genSalt(10, function(err, salt) {
console.log(salt);
bcrypt.hash(password, salt, function(err, hash) {
// Store hash in your password DB.
console.log(hash); //no error
queryString = "insert into user(email,name, phone, password, course_id, dpt_id) values('%s','%s','%s','%s','%s','%s')";
query = util.format(queryString, email, name, phone, hash, courseId, dptId);
databaseUtils.executeQuery(query, function() {
this.redirect('/');
});
});
});
Problem is, where does your redirect function come from? You're going to loose context on this unless you bind the functions. Do fat arrow functions work in your setup? If so you could do it that way.

BCrypt hash error

this is my code for hashing password and for compare existing password into existing module with a password that has been sended on body request:
//hash password of document that use this schema
bcrypt.hash(user.password, null, null, function (err, hashed) {
if (err) {
throw err;
} else {
user.password = hashed;
//next api
next();
}
})
});
userSchema.methods.comparePassword = function (password) {
//refer at userSchema
var user = this;
//return method of bcryot library that compare two string: original password and password hashed
return bcrypt.compareSync(password, user.password);
};
But compare this error message:
Uncaught, unspecified "error" event. (Not a valid BCrypt hash.)
Resolved !!! Into the database i have a lot of user's password not hashed and when i try to login, with bcrypt.compareSync (password, user.password); it expected that has been hashed password.
You're using null twice. I'd wager that you've wrapped this function inside the bcrypt.genSalt function(if you haven't , do so). You need to pass it the bcrypt salt where the first null is written.
Here's a full example:
userSchema.pre('save', function (next) {
const SALTROUNDS = 10; // or another integer in that ballpark
const user = this;
if(!user.isModified('password')) {
return next();
}
bcrypt.genSalt(SALTROUNDS, (err, salt) => {
if (err) { return next(err); }
bcrypt.hash(user.password, salt, null, (error, hash) => {
if (error) { return next(error); }
user.password = hash;
next();
});
});
});

Create user, callback

1st post. I'm a student studying js and came across this problem with a callback that I'm not sure how to structure properly.
var bcrypt = require('bcrypt-nodejs');
var users = db.collection("users");
this.addUser = function(username, password, email, callback) {
"use strict";
// Generate password hash
var salt = bcrypt.genSaltSync();
var password_hash = bcrypt.hashSync(password, salt);
// Create user document
var user = {'_id': username, 'password': password_hash};
// Add email if set
if (email != "") {
user['email'] = email;
}
// NOT SURE What to write here
callback(Error("addUser Not Yet Implemented!"), null);
}
First of all: do not use sync methods if possible, use callbacks instead.
var bcrypt = require('bcrypt-nodejs');
var users = db.collection("users");
this.addUser = function(username, password, email, callback) {
"use strict";
// Generate password hash
bcrypt.genSalt(function(err, salt) {
if (err) {
callback(err);
return;
}
bcrypt.hash(password, salt, function(err, password_hash) {
if (err) {
callback(err);
return;
}
// Create user document
var user = {
'_id': username,
'password': password_hash
};
// Add email if set
if (email != "") {
user['email'] = email;
}
// NOT SURE What to write here
callback(null);
});
});
}
and please ask precise questions.
If I understand propperly, you dont know how to handle a callback?
you simply pass the function that will be called after all work in your function is done as the parameter callback. when your done, you call the callback with the wanted parameters.
here it's the err object in case of an error or null if there is no error.
If you want to pass your created user to the callback, just replace
callback(null); with callback(null, user);

Resources