Reputation: 3886
I am trying to get login service prototype up, as first step in server-side part of this personal project I am working on. I attempt to reference the code here and here, as it is out of the Learning Node.js book (by Marc Wandschneider), which has been out for three years, and is thus already proven to work.
My actual server code can be found at this StackOverflow link. I skip the database part because I have trouble getting it up, and instead, I make my ./helpers/users.js
look like:
exports.version = "0.0.1";
var async = require('async'),
bcrypt = require('bcrypt');
var helpers = require('./helpers.js');
function User(id, email, displayName, password, deleted)
{
this.userID = id;
this.email = email;
this.displayName = displayName;
if (User.connectedToDatabase) this._password = password;
else
{
bcrypt.genSalt(10, function (err, salt) {
// this, for some reason, isn't getting called. Literally, I never see "I'm here"
console.log("I'm here...");
bcrypt.hash(password, salt, function (err, hash) {
if (!err)
{
this._password = hash;
console.log("this._password == " + this._password);
}
else
{
console.log("Error occurred: ");
console.log(err);
}
})
});
}
//this._password = password;
this.deleted = deleted;
}
User.connectedToDatabase = false;
User.prototype.userID = 0;
User.prototype.email = null;
User.prototype.displayName = null;
User.prototype._password = null;
User.prototype.deleted = false;
User.prototype.checkPassword = function (password, callback)
{
bcrypt.compare(password, this._password, callback); // returns false
}
User.prototype.responseObject = function() {
return {
id: this.userID,
email: this.email,
displayName: this.displayName
};
}
exports.login = function (req, res) {
var dblessPrototype = true;
// get email address from req.body, trim it and lowercase it
var email = req.body.emailAddress ?
req.body.emailAddress.trim().toLowerCase() :
"";
// begin the login process
async.waterfall([
// prelimninary verification: make sure email,password are not empty, and that email is of valid format
function(cb)
{
// if no email address
if (!email)
{
// send error via cb
cb(helpers.missingData("email_address"));
}
// if '@' not found in email address
else if (email.indexOf('@') == -1)
{
// then email address is invalid
cb(helpers.invalidData("email_address"));
}
// if password is missing from req.body
else if (!req.body.password)
{
// tell next function about that
cb(helpers.missingData("password"));
}
// we are ready to move on otherwise
else cb(null);
},
// TODO: lookup by email address
// check the password
function (userData, cb)
{
var u;
if (dblessPrototype)
{
u = new User(0,
"[email protected]",
"SampleAdmin",
"Sample0x50617373");
}
else
{
u = new User(userData);
}
console.log("u._password == " + u._password);
console.log("req.body.password == " + req.body.password);
u.checkPassword(req.body.password, cb);
},
// time to set status of authenticiation
function (authOK, cb)
{
console.log("authOK == " + authOK);
if (!authOK)
{
cb(helpers.authFailed());
return;
}
// set status of authenticiation in req.session
req.session.loggedIn = true;
req.session.emailAddress = req.body.emailAddress;
req.session.loggedInTime = new Date();
}
],
function (err, results)
{
if (err)
{
console.log(JSON.stringify(err, null, '\t'));
if (err.code != "already_logged_in")
{
helpers.sendFailure(res, err);
console.log("Already logged in...");
}
}
else
{
helpers.sendSuccess(res, { loggedIn: true });
console.log("Log in successful...");
}
});
}
In the checking of the password, u._password
is null (it never gets set, which means that the asynchronous bcrypt.genSalt()
code isn't getting called. Also, neither the last function in the array that is the first parameter of async.waterfall()
nor the function that is the last parameter of async.waterfall()
are getting called.
Why aren't these functions getting invoked, and what can I do about it?
EDIT: I made the asynchronous password encryption synchronous, by replacing
bcrypt.genSalt(10, function (err, salt) {
// this, for some reason, isn't getting called. Literally, I never see "I'm here"
console.log("I'm here...");
bcrypt.hash(password, salt, function (err, hash) {
if (!err)
{
this._password = hash;
console.log("this._password == " + this._password);
}
else
{
console.log("Error occurred: ");
console.log(err);
}
})
});
with this._password = bcrypt.hashSync(password, bcrypt.genSaltSync(10));
It gets to the password-comparison part, hangs for a while, and then to the next (the last) element of the array, without printing anything to the console. It's as if that element got skipped.
Upvotes: 0
Views: 112
Reputation: 5583
EDIT after downloading the full app and messing around with it at home.
I changed your code to include a setTimeout method and also enforced the this
context in the User function. Running all the code on my own machine which I downloaded from your git repo I got it to the point where user is authenticated and the app looks for index.html which is not there. So the authentication is working!
The code is not waiting for the salt and hash to finish before continuing. If you were writing to a db you could do this 'pre' save and use a promise. But this will give you a work around for now.
function (cb, userData)
{
var u;
if (dblessPrototype)
{
var authOK;
u = new User(0,
"[email protected]",
"SampleAdmin",
"Sample0x50617373");
setTimeout(function () {
console.log("u._password == " + u._password);
console.log("req.body.password == " + req.body.password);
console.log(u)
u.checkPassword(req.body.password, function(err, res) {
if (err) {
console.log(err)
} else {
console.log(res)
// USER AUTHENTICATED
cb(null, true)
}
return res;
});
}, 1000)
}
else
{
u = new User(userData);
}
}
Assign this
to self
inside the User object generator:
function User(id, email, displayName, password, deleted)
{
this.userID = id;
this.email = email;
this.displayName = displayName;
var self = this
if (User.connectedToDatabase) this._password = password;
else
{
bcrypt.genSalt(10, function (err, salt) {
// this, for some reason, isn't getting called. Literally, I never see "I'm here"
console.log("I'm here...");
bcrypt.hash(password, salt, function (err, hash) {
if (!err)
{
self._password = hash;
console.log("this._password == " + self._password);
}
else
{
console.log("Error occurred: ");
console.log(err);
}
})
});
}
// this._password = password;
this.deleted = deleted;
}
I should add that using the setTimeout is not a recommended approach, but a way of demonstrating what the problem is. Ideally this should be done using a callback, promise, or generator.
Upvotes: 1