Data and salt arguments required - node.js

I'm trying to hash the password of admin in my site. I have searched and found out that this error is because of being null or undefined the value that we want to hash it.
here is my code, whenever I console.log(admin) it returns {}, I don't know why.
adminSchema.pre('save', (next) => {
var admin = this;
console.log(admin)
bcrypt.hash(admin.password, 10, (err, hash) => {
if (err) return next(err);
admin.password = hash;
next();
});
});
var adminModel = mongoose.model('Admin', adminSchema);
module.exports = adminModel;
server side code:
var adminModel = require('./../models/admins');
router.post('/register', (req, res) => {
var newAdmin = {
adminName: req.body.adminName,
faculty: req.body.faculty,
email: req.body.email,
password: req.body.password
}
adminModel.create(newAdmin, (err, admin) => {
if (err) {
console.log('[Admin Registration]: ' + err);
}
else {
console.log('[Admin Registration]: Done');
req.session.adminId = admin._id;
res.redirect('/admin/submitScore')
}
})
});
Unfortunately, I can't find the reason of that the console.log(admin) is empty. I would be thankful if anyone could help me.

The keyword this changes scope when used in arrow functions. See more here. This is not a problem in your express route, but in your mongoose middleware it is. Change your function to not use this or make an old fashioned function(){}

Related

Why the hash value is undefined?

Value of salt is logged correctly but hash is giving undefined.
I am unable to figure out what is incorrect here.
users.js is a model and the other code is a controller.
users.js
UserSchema.statics.updateOTPOnDatabase = function(mobile,otp){
bcrypt.genSalt(10, function(err, salt){
console.log(salt); //this works fine
bcrypt.hash(otp, salt, function(err, hash){
console.log(hash); //giving undefined
});
})
}
login.js
exports.login = (req, res) => {
const mobile = _.pick(req.body, ['mobile_number']);
User.findByMobile(mobile).then((user) => {
const otp = Math.round(Math.random()*9000 + 1000);
User.updateOTPOnDatabase(user.mobile_number, otp).then(res => {
console.log(res);
}).catch(err => {
var response = {
status: 'failure',
message: err.message
};
res.send(response);
});
}).catch(err => {
var response = {
status: 'failure',
message: err.message
}
res.send(response);
});
};
The only issue I can see is with your updateOTPOnDatabase function, as in this function you are accepting 1 parameter otp but when you call this function you are passing 2 parameters user.mobile_number,otp
This is your definition
UserSchema.statics.updateOTPOnDatabase = function(otp){ }
This is how you are invoking it
User.updateOTPOnDatabase(user.mobile_number, otp) { }
So you might need to fix 1 or the other and also check if user.mobile_number is not returning undefined or null
Edit 2
As per the comment and per Github code, a string is expected so you can convert otp to string by using .toString() method
UserSchema.statics.updateOTPOnDatabase = function(mobile,otp){
bcrypt.genSalt(10, function(err, salt){
console.log(salt);
bcrypt.hash(otp.toString(), salt, function(err, hash){
console.log(hash);
});
})
}

MonogDB resets the saved value automatically after some time

I am using node.js to build a small game and to store states/token i am using online database mongolab. I generate the token with the following function:
UserSchema.methods.generateAuthToken = function() {
var user = this;
var access = "auth";
var token = jwt.sign({_id: user._id.toHexString, access}, "abc123").toString();
user.tokens = user.tokens.concat({access, token});
return user.save().then(() => {
return token;
});
};
The save function is :
UserSchema.pre("save", function(next) {
var user = this;
console.log("Being saved");
if (user.isModified("password")) {
console.log("Being modified");
bcrypt.genSalt(5, (err, saltvalue) => {
bcrypt.hash(user.password, saltvalue, (err, hash) => {
user.password = hash;
next();
});
});
} else {
next();
}
});
If i stay at a certain page for sometime(delay) then database resets the token to empty array.
However i have inserted everywhere console.logs to see if the server is trying to reset the value but the database does it automatic.
If someone has knowledge about this kind of error,then it will be of great help.

NodeJS, getting username of logged in user within route

I am getting in to NodeJS and have followed some video tutorials for making stuff, to understand NodeJS and Express. It turned more in to copying as little were explained, so tried to make my own thing using what I learned and so on.
Making a simple login function with PassportJS, ExpressJs and Mongoose.
The login and stuff works, and I can get the username of the currently logged in user and display it if I define it within the main app.js using this:
app.get("/stuff", (req,res) => {
res.render("stuff.html", {username:req.user.username});
});
Now if I want to make nice and structured by using router, I cannot get it to work. It throws error saying username is undefined, making page unable to render. The router itself works if I don't pass any variable or use variables I know will work (e.g. var x = "Hello"; res.render … {msg:x});).
Part of the app.js that handle routes:
var stuff = require("./routes/stuff");
app.use("/stuff", stuff);
module.exports.app;
I've tried to cont x = require("…") basically everything that is in the app.js in this stuff.js file, but to no avail, so removed everything but express + routes to get fresh start.
How do I pass the username that is working in app.js in to the routed file? Preferably make it automatically do to every page if possible, using app.get("*")… or something.
Entire stuff.js:
/* Routes */
const express = require("express");
const router = express.Router();
/* Stuff */
router.get("/", function(req, res, next) {
res.render("stuff.html", {username:req.user.username});
console.log(req.user.username);
next();
});
/* Bottom */
module.exports = router;
Login section of app.js:
app.post('/login',
passport.authenticate('local',
{
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: 'Wrong login'
}
), function(req,res) {
console.log("Hello " + req.user.username);
});
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,callback) {
User.getUserByUsername(username, function(err,user) {
if(err) throw err;
if(!user) {
return callback(null, false, {msg: "shit"});
}
User.comparePassword(password, user.password, function(err,isMatch) {
if(err) return callback(err);
if(isMatch) {
return callback(null, user);
} else {
return callback(null, false, {msg:"Something"});
}
});
});
}));
The users.js file for handling registering new users, if that's relevant:
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/users");
const db = mongoose.connection;
mongoose.Promise = global.Promise;
const bcrypt = require("bcryptjs");
/* Data schema */
const userSchema = mongoose.Schema({
name: {
type: String
},
username: {
type: String,
index: true
},
password: {
type: String
},
email: {
type: String
}
});
var User = module.exports = mongoose.model("User", userSchema);
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.getUserById = function(id, callback) {
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback) {
var query = {username: username};
User.findOne(query, callback);
}
module.exports.comparePassword = function(testPw, hash, callback) {
bcrypt.compare(testPw, hash, function(err,isMatch) {
callback(null,isMatch);
});
}
As far as I understand you are trying to pass your username to the preferably every file including your router file. What I do for this to use middleware in app.js to pass every page. Or you can simply implement passport implementation in the other page as well which could be useless i guess.
app.use(function(req,res,next){
res.locals.currentUser=req.user
next()
}
Then, you can use use your currentUser in every page when you try to render.
I encountered the same issue, probably after following the same tutorials...
I found that the function you need in the app.js is:
app.get('*', function(req, res,next){
res.locals.user = req.user || null;
next();
})
it should be in the app.js already. Now, in all of the other page you should be able to use req.user.username

Mongoose findOne function is not working with authentication

I am working on a model here:
// user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
// Define collection and schema for Users
let User = new Schema(
{
firstName: String,
lastName: String,
emailaddress: String,
password: String,
},
{
collection: 'users'
}
);
// authenticate input against database documents
User.statics.authenticate = ((emailaddress, password, callback) => {
User.findOne({ emailaddress: emailaddress })
.exec(function(error, user){
if(error){
return callback(error)
} else if (!user){
console.log('User not found!');
}
bycrypt.compare(password, user.password, function(err, result){
if(result === true){
return callback(null, user);
} else {
return callback();
}
})
})
});
module.exports = mongoose.model('User', User);
As you can see on my model I put the User.statics.authenticate on my codes to do some authentication. And then on my login.js route file:
const path = require('path');
const express = require('express');
const router = express.Router();
const db = require('../../database/index');
const axios = require('axios');
const User = require('../../database/models/user');
router.get('/', (req, res) => {
console.log('hi there this is working login get');
});
router.post('/', (req, res) => {
var emailaddress = req.body.emailaddress;
var password = req.body.password;
if( emailaddress && password ){
User.authenticate(emailaddress, password, function(err, user){
if(err || !user){
console.log('Wrong email or password!');
} else {
req.session.userId = user._id;
return res.redirect('/');
}
});
} else {
console.log('both fields are required...');
}
});
module.exports = router;
I called the function and then User.authenticate function and also I created the route for root w/c is the sample that I want to protect and redirect the user after login:
router.get('/', (req, res) => {
if(! req.session.userId ){
console.log('You are not authorized to view this page!');
}
User.findById(req.session.userId)
.exect((err, user) => {
if(err){
console.log(err)
} else {
res.redirect('/');
}
})
});
Upon clicking submit on my react form it returns this error:
TypeError: User.findOne is not a function
at Function.User.statics.authenticate (/Users/mac/Documents/monkeys/database/models/user.js:35:8)
I checked the Mongoose documentation and it seems I am using the right syntax.Any idea what am I doing wrong here? Please help! Sorry super beginner here!
PS. I've already installed and set up the basic express session too.
UPDATES:
I remove the arrow function from my call and use this.model.findOne but still get the typerror findOne is not a function
// authenticate input against database documents
User.statics.authenticate = function(emailaddress, password, callback){
this.model.findOne({ emailaddress: emailaddress })
.exec(function(error, user){
if(error){
return callback(error)
} else if (!user){
console.log('User not found!');
}
bycrypt.compare(password, user.password, function(err, result){
if(result === true){
return callback(null, user);
} else {
return callback();
}
})
})
};
findOne is a method on your User model, not your user model instance. It provides its async results to the caller via callback:
User.findOne({field:'value'}, function(err, doc) { ... });

passport-local-mongoose set new password after checking for old password

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.

Resources