I am trying to implement a authentication system for my website using MEAN however I have run into a relatively strange problem. I am able to register users and duplicate usernames can be identified. However, I cannot get logging into the website working. When I search the mongo database using the command line, I do not get anything. This is what my mongo output looks like.
>> show users
>>
The database has the username somewhere... so how do I get the users to be properly displayed? Why is that user is undefined when I try to log in even though I know the username is in the database?
var crypto = require('crypto');
var mongoose = require('mongoose');
var User = mongoose.model('User');
function hashPW(pwd) {
return crypto.createHash('sha256').update(pwd).digest('base64').toString();
};
module.exports.signup = function (req,res) {
var user = new User({username:req.body.usernmae});
console.log('made it here');
user.set('hashed_password', hashPW(req.body.password));
user.set('email', req.body.email);
user.save(function (err) {
if (err) {
try {
if (err.code==11000) res.render('signup', {message: 'Sorry, someone has that username already.'})
} catch(e) {
}
console.log(err);
//res.redirect('/signup');
} else {
req.session.user = user.id;
req.session.username = user.username;
req.session.msg = 'Authenticated as ' + user.username;
res.redirect('/');
}
});
};
module.exports.login = function (req,res) {
User.findOne({ username: req.body.username })
.exec(function(err,user) {
console.log(user);
console.log(err);
console.log(hashPW(req.body.password.toString()));
if (!user) {
err = 'User Not Found.';
} else if ( user.password === hashPW( req.body.password.toString() ) ) {
req.session.regenerate(function() {
req.session.user = user.id;
req.session.username = user.username;
req.session.msg = 'Authenticated as ' + user.username;
res.redirect('/');
});
} else {
err = 'Authentication failed.';
}
if (err) {
console.log(err);
req.session.regenerate(function() {
req.session.msg = err;
res.redirect('/login');
});
}
});
};
I notice that there's a typo in the provided code.
var user = new User({username:req.body.usernmae});
Should likely read
var user = new User({username:req.body.username});
This probably meant the name failed to set thus putting a junk user into your DB.
Also, regarding your command in the Mongo Shell, Neil's answer covered that the show command is not actually useful here. The reference for db.collection.find() is here.
silly mistake. the field is not password but hashed_password.
{ email: 'somerandomemail#gmail.com',
hashed_password: 'A8ctR3JAA84DWTmYXEAhxEEP1bTtAidaoyWArKHtk2g=',
username: 'Szpok',
_id: 54c09c458c4eccc90b9c4bb5,
__v: 0 }
Related
I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
I am using passportjs to handle auth of my app.
Once the user is logged in, I want to add the possibility to change the password from inside the app.
this is in my controller:
$http.post('/change-my-password',{oldPassword: $scope.user.oldpassword, newPassword: $scope.user.newpassword})
.then(function (res) {
if (res.data.success) {
// password has been changed.
} else {
// old password was wrong.
}
});
and this is my route handler in express nodejs in backend:
router.post('/change-my-password', function (req, res) {
if (!req.isAuthenticated()) {
return res.status(403).json({
success: false
});
}
UserSchema.findById(req.user._id, function(err, user){
if (err) return res.status(200).json({success: false});
user.validatePassword(req.body.oldPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
user.setPassword(req.body.newPassword, function() {
if (err || !user) {
return res.status(200).json(
{
success: false
}
)
}
user.save(function(err) {
if (err) return res.status(200).json({success: false});
req.login(user, function (err) {
if (err) return res.status(200).json({success: false});
return res.status(200).json({success: true});
});
});
});
});
});
});
here is my user schema model:
// user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema({
email: String,
password: String,
confirmStatus: Boolean,
token: String,
registerAt: Number
});
UserSchema.methods.validatePassword = function (password, callback) {
this.authenticate(password, callback);
};
UserSchema.plugin(passportLocalMongoose,
{
usernameField: 'email'
});
module.exports = mongoose.model('users', UserSchema);
the problem:
I find my user by Id in my mongoose schema UserSchema then I should check if the oldPassword is valid or not, and then I set the new password.
I successfully find the user and the set the new password. But the part that should check for comparison of the old password field, doesn't work at all. Whatever I enter in the old password field gets accepts as OK and that step is skipped. Whereas, it should throws an error saying that the old password is wrong.
I am also advised to use sanitizedUser in order not to show my salt and etc.
Question is: how can I first do the comparison check of the old password and then do the set new password step? If possible, how can I use the sanitize? And how can I check if the user is not entering the same password as the new password? or if possible, saying that the new password is very similar to the old one?
You can implement the it using the new feature added 3 days ago:
just use the changePassword method, and it handles it through this:
schema.methods.changePassword = function(oldPassword, newPassword, cb) {
if (!oldPassword || !newPassword) {
return cb(new errors.MissingPasswordError(options.errorMessages.MissingPasswordError));
}
var self = this;
this.authenticate(oldPassword, function(err, authenticated) {
if (err) { return cb(err); }
if (!authenticated) {
return cb(new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
self.setPassword(newPassword, function(setPasswordErr, user) {
if (setPasswordErr) { return cb(setPasswordErr); }
self.save(function(saveErr) {
if (saveErr) { return cb(saveErr); }
cb(null, user);
});
});
});
};
so in your code, you need to replace the validatePassword method by this:
user.changePassword(req.body.oldPassword,req.body.newPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
hope this works for you.
This is my code for a login route, I am trying to call docs._id but it gives me back null. When I call just docs it shows this on server side:
[ { _id: EX unique id,
username: { user: 'EXUsername', search: 'exusername' },
pass: 'EXpassword',
email: 'EXemail' } ]
I am really confused on why I can't call docs.pass or docs._id at all but docs itself has stuff in it and I can call docs and it gives data back.
my code in node.js:
router.post('/loginUser', function(req, res){
CheckingLogin(req.db, req.body.username, req.body.password, function(bool, docs){
if(bool && exists(docs)){
var err = false;
var handleError = function(e){
if(err){
err = true;
var eDoc = {};
eDoc.errorCode = 1;
eDoc.error = e;
console.log(eDoc);
}
}
/////problem here//////////////////////////////////
var userID = docs._id;
console.log(docs._id);
if(userID != null){
if(!err){
tokens.db = req.db;
tokens.refresher.create(userID, function(token){
console.log('in token refresher ' + docs);
res.send('1');
});
}else{
console.log('Token err = true');
}
}else{
console.log('Creator token = null');
res.send('0');
}
}else{
res.send('0');
}
});
});
Hey its an array of object so just get it like this docs[0]._id it will work for you.
I am learning NodeJs Mean Stack and was working with Mean.Js sample app,
Everything works fine and I am able to create first user with the sign up form. But creating another user always give me "User name Alreay Exists" message. I've applied check on the code before save to see if user already exists ore not and mongoose findOne method returns me null(and he is right because user with the provided username does not exist) but save method still gives error, user already exist
here is my sign up code:
/**
* Signup
*/
exports.signup = function(req, res) {
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
// Init Variables
var user = new User(req.body);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
User.findOne({
"username": user.username
}, function(oerr, dbUser) {
if (oerr) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err) + ' user Name' + user.username
});
}
if (dbUser === null) {
// Then save the user
user.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err) + ' user Name' + user.username
});
} else {
// Remove sensitive data before login
user.password = undefined;
user.salt = undefined;
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
} else {
return res.status(400).send({
message: "This User Already Exist Buddy. " + ' user Name' + user.username
});
}
});
};
Thanks
I want to automatically generate user accounts by generating a random username and password, and then the user is logged in automatically (the user doesn't know his username/password, his browser just stores the session cookie).
Passport functions as middleware, so how can I authenticate the user I just generated? Or, would it be better to somehow redirect to my app.post('/login') route and send those variables? (But somehow sending those to the browser, just to be sent back to the server doesn't seem very secure or efficient).
app.get('/signup', function(req, res) {
if(req.isAuthenticated()) { res.redirect('/'); }
else {
var today = new Date();
var weekDate = new Date();
weekDate.setDate(today.getDate() + 7);
var key1 = Math.random().toString();
var key2 = Math.random().toString();
var hash1 = crypto.createHmac('sha1', key1).update(today.valueOf().toString()).digest('hex');
var hash2 = crypto.createHmac('sha1', key2).update(weekDate.valueOf().toString()).digest('hex');
var newUser = new models.User({
username: hash1,
password: hash2,
signupDate: today,
accountStatus: 0,
expirationDate: weekDate,
});
newUser.save(function(err) {
if(err) {}
console.log("New user created.");
//HOW CAN I PASS USERNAME AND PASSWORD ARGUMENTS???
passport.authenticate('local')();
res.redirect('/login');
})
}
});
Replace your call to passport.authenticate('local')(); with
req.logIn(user, function(err) {
if (err) { return next(err); }
//copied from the docs, you might want to send the user somewhere else ;)
return res.redirect('/users/' + user.username);
});
and let me know how that goes.
the answer by rdrey was very helpful. One detail that might be obvious to most but was not to me is that model .save () gets err and the record in the callback. So the pattern in its entirety is
newuser.save(function(err,user) {
req.logIn(user, function(err) {
if (err) { return next(err); }
//copied from the docs, you might want to send the user somewhere else ;)
return res.redirect('/users/' + user.username);
});