I've been struggling last week with error handling, especially error handling in Node.js modules. So first, here is my code:
user.js route
router.post('/register', function(req, res) {
var newUser = new User({
firstname: req.body.firstname,
lastname: req.body.lastname,
email: req.body.email,
password: req.body.password,
});
User.addUser(newUser, function(err, user) {
if(err) {
return next(err)
} else if(user) {
res.status(403).send('User already exists');
} else {
res.sendStatus(200);
}
});
});
user.js module
module.exports.addUser = function(newUser, cb) {
User.findOne({ email: newUser.email }, function(err, user) {
if(err) {
cb(err);
} else if(user) {
cb(null, user);
} else {
bcrypt.genSalt(10, function(err, salt) {
if(err) {
cb(err);
} else {
bcrypt.hash(newUser.password, salt, function(err, hash) {
if(err) {
cb(err)
} else {
newUser.password = hash;
newUser.save(function(err, newUser) {
if(err) {
cb(err);
} else {
cb(null, false);
}
});
}
});
}
});
}
});
}
Everytime if there is error inside user.js module, call callback function and handle error inside user.js route. This works, but that mess inside my module doesn't look good, because there is so many if-else statements..
Is there better approach, or do I have to check everytime if there is error?
You could simplify your code to something like:
module.exports.addUser = function(newUser, cb) {
User.findOne({ email: newUser.email }, function(err, user) {
if(err) {
cb(err);
return;
}
if(user) {
cb(null, user);
return ;
}
bcrypt.genSalt(10, function(err, salt) {
if(err) {
cb(err);
return;
}
bcrypt.hash(newUser.password, salt, function(err, hash) {
if(err) {
cb(err)
return;
}
newUser.password = hash;
newUser.save(function(err, newUser) {
if(err) {
cb(err);
return;
}
cb(null, false);
});
});
});
});
}
However, if I were you and as #Scimonster stated in his comment, this is a typical use case for promises that would allow you to write more readable code and avoid the callback hell
Related
I'm new to passport and I'm trying to create a "register" page. This actually works fine, and the log-in form as well. However, I want to check if the username entered already exists, and if it does, throw an error to the user. Here is my code so far:
expressApp.post("/register", function(request, response){
User.findOne({username: request.body.username}, function(err, user) {
if (err) {
return err;
}
if (user) {
}
else {
User.register(new User({
username: request.body.username,
type: "Student"}),
request.body.password, function(err){
if(err){
console.log(err);
}
passport.authenticate("local")(request, response, function(){
response.redirect("/");
});
});
}
})
});
However, If someone chooses a username that already exists, then i want to be able to tell them that there is an error.
It should look something like this.
expressApp.post("/register", function(request, response) {
User.findOne({
username: request.body.username
}, function(err, user) {
if (err) {
return err
} else if (user) {
//user.message = "User already exists!!"
response.statusCode = 409
return response.send({"message": "User already exists!!")
} else {
User.register(new User({
username: request.body.username,
type: "Student"
}),
request.body.password,
function(err) {
if (err) {
console.log(err);
}
passport.authenticate("local")(request, response, function() {
response.redirect("/");
});
});
}
});
});
I have used bcryptjs to hash my passwords and the registration of the user takes place fine.
But when I try to login, the bcrypt.compare function returns a false promise if the passwords contain numbers or other characters.
Everything works fine if the password is just letters.
I have searched everywhere but could not find a solution for this error?
Here are my files :
users.js (MODEL)
var mongoose = require("mongoose");
var bcrypt = require("bcryptjs");
var userSchema = mongoose.Schema({
name: String,
email: String,
password: String,
products: [{
type: mongoose.Schema.Types.ObjectID,
ref: "Product"
}],
username: String
});
var User = module.exports = mongoose.model("User", userSchema);
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(5,function(err,salt){
bcrypt.hash(newUser.password,salt,function(err,hash){
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
console.log(isMatch);
callback(null, isMatch);
});
}
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
app.js
app.use(passport.initialize());
app.use(passport.session());
var LocalStrategy = require("passport-local").Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({username:username}, function(err, user){
if(err) throw err;
if(!user) {
return done(null,false, {message: 'Unknown user'});
}
User.comparePassword(password,user.password,function(err, isMatch){
if(err) {
console.log("ERR1");
return done(err);
}
if(isMatch) {
console.log("MATCH");
return done(null,user);
} else {
console.log("NOT VALID");
return done(null, false, {message: 'Invalid password'});
}
});
});
}
));
passport.serializeUser(function(user,done){
done(null,user.id);
});
passport.deserializeUser(function(id,done){
User.getUserById(id, function(err,user){
done(err,user);
})
})
users.js (ROUTE)
router.post("/login", passport.authenticate('local', function (error, user, info){
if(error) {
console.error(error);
console.log('Failed login:');
}
if (user === false) {
console.log("user not found");
} else {
// handle successful login ...
console.log("logged in");
}
}), function (req, res) {
res.send(req.user);
});
router.post("/signup", function (req, res) {
console.log(req.body);
var password = req.body.password;
var password2 = req.body.password2;
if(password == password2) {
var newUser = new User ({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.username
});
User.createUser(newUser, function(err, user){
if(err)
throw err;
console.log(user);
res.send(user).end();
});
} else {
res.status(500).send("{errors: \"Passwords don't match\"}").end()
}
});
Whenever I enter a password that contains numbers, I get
false
NOT VALID
user not found
I'm sorry if I have done or not done something extremely simple. This is my first time using bcryptjs.
All answers are appreciated!
In the users.js file at the comparePassword function inside the compare method of bcrypt you are throwing the error instead of passing it to the callback. Maybe if you make this change:
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
console.log(isMatch);
callback(err, isMatch);
});
}
You will see what kind of error it's generated by the case when you introduce numbers in your passwords.
I have this problem in the time i am in admin routes and i try to add a new article or a new image in my database , the same things happen even when i want to update something ....
this is my codes ?
exports.isAdmin = (req, res, next) => {
if (req.isAuthenticated() && res.locals.user.admin == 1) {
next();
} else {
req.flash("danger", "please log in as admin");
res.redirect("/users/login");
}
}
var LocalStrategy = require("passport-local").Strategy;
var User = require("../models/user");
var bcrypt = require("bcryptjs");
module.exports = function (passport) {
passport.use(new LocalStrategy(function (username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err)
console.log(err);
if (!user) {
return done(null, false, { message: "No user found" });
}
bcrypt.compare(password, user.password, function (err, isMatch) {
if (err) console.log(err);
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Wrong password" });
}
})
});
}));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
}
Anyone one can help the problem is i think in res.locals.user.admin but in other admin routes works ok but only when i want to add a new and edit a think happen this problem
I am trying to make a user account system in node and am using bcrypt for hashing passwords. My syntax seems to be correct, and there is no error thrown in the console, however still the passwords aren't getting encrypted when stored in the database(mongo db).
This is my user.js file in models:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
mongoose.connect('mongodb://localhost/nodeauth');
var db = mongoose.connection;
// User
var UserSchema = mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String,
required: true,
bcrypt: true
},
email: {
type: String
},
name: {
type: String
},
profileImage: {
type: String
}
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.comparePassword = function(candidatePassword, hash, callback) {
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) return callback(err);
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback) {
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback) {
var query = {username: username};
User.findOne(query, function(err, user) {
callback(err, user);
});
}
module.exports.createUser = function(newUser, callback) {
bcrypt.hash(newUser.password, 10, function(err, hash) {
if(err) throw err;
// Set hashed password
newUser.password = hash;
// Create user
newUser.save(callback);
});
}
And this is the relevant part of users.js file in routes which is used in the login section to check whether the username and password match(which is also not working):
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(function(username, password, done) {
User.getUserByUsername(username, function(err, user) {
if(err) throw err;
if(!user) {
console.log('Unknown user');
return done(null, false, {message: 'Unknown User'})
}
User.comparePassword(password, user.password, function(err, isMatch) {
if(err) throw err;
if(isMatch) {
return done(null, user);
} else {
console.log('Invalid Password');
return done(null, false, {message: 'Invalid Password'});
}
});
});
}));
router.post('/login', passport.authenticate('local', {successRedirect: '/', failureRedirect:'/users/login', failureFlash:'Invalid username or password'}), function(req, res) {
console.log('Authentication Successful');
req.flash('success', 'You are logged in');
res.redirect('/');
});
module.exports = router;
I am not able to understand why the passwords aren't getting encrypted, and even then, why is comparePassword always returning a failure, thus making authentication fail every time as well.
Is there anything I am missing?
I'm trying to send some form data, but I get this error using express.js:
Can't set headers after they are sent.
This is my code so far:
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.send(200);
});
console.log(res.send(user)) //HERE IS WHERE I GET THE ERROR
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});
By console logging res.send(user) you are sending again. You can send once and once only.
app.post('/api/users/profile/:username', isAuthenticated, userUploads, function(req, res, next) {
if (req.params.username) {
User.findOne({ username: req.params.username }, function(err, user) {
if (err) return next(err);
user.profile.name = req.body.name;
user.profile.gender = req.body.gender;
var files = req.files.file;
if (files){
if (files.length > 0){
for (f in files){
user.profile.pictures.push(files[f])
}
}else{
user.profile.pictures.push(files)
}
}
user.save(function(err) {
if (err) return next(err);
res.status(200).send(user);
});
});
}else{
return res.send(400, { message: 'User does not exist!!' });
}
});