User is not defined in Node.js Express controller function - node.js

When I click reset password button it throws user is not defined in the command prompt for back-end. I am using Mongoose with Node.js Express. Here is the code for both model and controller.
controller
const User = require('../models/userModels');
const passwordResetToken = require('../models/resettokenModels');
async ResetPassword(req, res) {
if (!req.body.email) {
return res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Email is required' });
}
const userEmail = await User.findOne({
email: Helpers.lowerCase(req.body.email)
});
if (!userEmail) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Email does not exist' });
}
var resettoken = new passwordResetToken({ _userId: user._id, resettoken: crypto.randomBytes(16).toString('hex') });
resettoken.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
var transporter = nodemailer.createTransport({
service: '"SendGrid"',
auth:
{
user: 'email',
pass: 'password'
}
});
var mailOptions = {
from: 'email',
subject: 'Node.js Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + resettoken + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
}
transporter.sendMail(mailOptions
)
})
.catch(err => {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
},
And here is the model
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const resettokenSchema = new mongoose.Schema({
_userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
resettoken: { type: String, required: true },
createdAt: { type: Date, required: true, default: Date.now, expires: 43200 },
});
module.exports = mongoose.model('passwordResetToken', resettokenSchema);
USer model
const userSchema = new mongoose.Schema({
username: { type: String },
email: { type: String },
password: { type: String },
...
...
Error is showing in this line inside controller
var resettoken = new passwordResetToken({ _userId: user._id, resettoken: crypto.randomBytes(16).toString('hex') });
What can cause such an issue?

As mentioned in comments you are not setting user to anything.
edit:
This is using your user schema, you have shown above that the user schema has username, email, password, ect.
const user = await User.findOne({
email: Helpers.lowerCase(req.body.email)
});
Your reset token needs to do this:
var resettoken = new passwordResetToken({ _userId: user._id, resettoken: crypto.randomBytes(16).toString('hex') });
Your nodemailer needs to do this:
var mailOptions = {
from: 'mail#mail.com',
to: user.email,
subject: 'Node.js Password Reset',
text: 'some message'
}
transporter.sendMail(mailOptions)

Related

VS Code Terminal Shows Error: data and salt arguments required when i hit the api

This is My User Model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = mongoose.Schema({
contact: {
type: Number,
required: true,
},
email: {
type: String,
required: true,
unique: true
},
score: {
type: Number,
default:0,
},
password: {
type: String,
required: true,
},
role: {
type: String
},
blocked: {
type: Boolean, default: false
}
}, { timestamp: true }
)
userSchema.statics.hashPassword = function hashPassword(password){
return bcrypt.hashSync(password,10);
}
userSchema.methods.isValid = function(hashedpassword){
return bcrypt.compareSync(hashedpassword, this.password);
}
module.exports = mongoose.model('user',userSchema)
This is my Controller
const User = require('../models/user')
const Otp = require('../models/otp')
const jwt = require('jsonwebtoken')
const sendMail = require('../mail/mail')
const bcrypt = require('bcryptjs')
exports.getCheck = (req, res, next) => {
res.json({ msg: "All ok" })
}
exports.registerStudent = async (req, res) => {
// const x = await check(req,res,req.body.email);
const user = new User({
contact: req.body.phone,
email: req.body.email,
role: "student",
password: User.hashPassword(req.body.p1),
});
User.find({ email: req.body.email }, (err, users) => {
if (err) {
console.log("err in finding email ");
res.json({ msg: "some baler error!" });
}
if (users.length != 0) {
console.log("already user with this email");
res.json({ msg: "already user exist with this email!" });
}
else {
user.save((error, registeredUser) => {
if (error) {
console.log(error);
res.json({ msg: "some error!" });
}
else {
let payload = { subject: registeredUser._id }
let token = jwt.sign(payload, 'secretkey')
res.status(200).json({ token: token })
}
})
}
})
}
PLeasee HELP me out i'm getting confused
IGNORE
I ran into the same problem. 1. I saved my ATLAS_URI ID to a file called .env 2. My .env file was in the wrong directory, that's how the problem cause 3. Solution: I used "ls -a" command to make sure my .env file is in the same location as my server
IGNORE
PLeasee HELP me out i'm getting confused

Expressjs: Cannot POST /api/v1/users/ in Postman

I'm trying test an API that we are developing in Express. I'm testing it in Postman; and the app seems to start fine. But when I'm testing the users's API it return many error messages:
This is my model
User.js
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
name: {
type: String,
required: true,
min: 6,
max: 255
},
email: {
type: String,
required: true,
min: 6,
max: 1024
},
password: {
type: String,
required: true,
minlength: 6
},
subscribe: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
},
role: {
type: String,
required: false,
minlength: 4
},
address: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
nonce: {
type: String,
required: false,
minlength: 4,
defaultValue: ""
},
})
module.exports = mongoose.model('User', userSchema);
And this is my controller(user.js):
const Joi = require('#hapi/joi');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const nodemailer = require("nodemailer");
//models
const User = require('../models/user');
const expired_token = require('../models/expired_token');
module.exports = {
register: async (req, res) => {
const schemaRegister = Joi.object({
name: Joi.string().min(3).max(255).required(),
email: Joi.string().min(4).max(255).required().email(),
password: Joi.string().min(6).max(1024).required()
})
// validate user
const { error } = schemaRegister.validate(req.body)
if (error) {
return res.status(400).json({
data: error,
message: error.details[0].message,
status: "error"
})
}
const isEmailExist = await User.findOne({ email: req.body.email });
if (isEmailExist) {
return res.status(400).json({
data: "",
message: "email already registered",
status: "error"
})
}
// hash password
const salt = await bcrypt.genSalt(10);
const password = await bcrypt.hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
role: "user",
password: password
});
try {
const savedUser = await user.save();
console.log(savedUser);
const token = jwt.sign({
name: savedUser.name,
id: savedUser._id,
email: savedUser.email,
role: savedUser.role
}, process.env.TOKEN_SECRET);
res.status(200).json({
data: {
email:savedUser.email,
name: savedUser.name,
role: savedUser.role,
token: token
},
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error creating user",
status: "error"
});
}
},
login: async (req, res) => {
const schemaLogin = Joi.object({
email: Joi.string().min(6).max(255).required().email(),
password: Joi.string().min(6).max(1024).required()
})
const { error } = schemaLogin.validate(req.body);
if (error) {
return res.status(400).json({
data: error,
message: error.details[0].message,
status: "error"
})
}
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(400).json({
data: "",
message: "User not found",
status: "error"
})
}
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) {
return res.status(400).json({
data: {},
message: "Invalid password",
status: "error"
})
}
const token = jwt.sign({
name: user.name,
id: user._id,
email: user.email,
role: user.role
}, process.env.TOKEN_SECRET);
res.status(200).json({
data: {
email:user.email,
name: user.name,
token: token
},
message:"Successful request",
status: "success"
});
},
list_users: async (req, res) => {
const users = await User.find({});
res.status(200).json({
data: users,
message:"Successful request",
status: "success"
});
},
logout: async (req, res) => {
let user_info = req.decoded;
let token = req.token;
const expired_token_exists = await expired_token.findOne({ token: token });
if (expired_token_exists) {
return res.status(200).json({
data: "",
message: "user already logged out",
status: "success"
})
}
const data = new expired_token({
id_user: user_info.id,
token: token
});
try {
const saved_expired_token = await data.save();
res.status(200).json({
data: saved_expired_token,
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error creating user",
status: "error"
});
}
},
get_user_data: async (req, res) => {
let user_info = req.decoded;
try {
const user = await User.find({
_id:user_info.id
});
const user_json=user[0];
const user_data = {
id:user_info.id,
name:user_json.name,
email:user_json.email,
subscribe:user_json.subscribe,
role:user_json.role,
date:user_json.date
};
res.status(200).json({
data: user_data,
message:"Successful request",
status: "success"
});
} catch (error) {
res.status(401).json({
data: error,
message:"Error getting user data",
status: "error"
});
}
}
}
And this my routes(index.js):
const { func } = require('#hapi/joi');
const controller = require('../../v1/controllers/users');
const verifyToken = require('../../v1/utils').verifyToken;
module.exports = (router) => {
router.route('/list_users')
.get(verifyToken, controller.list_users);
router.route('/register')
.post(controller.register);
router.route('/login')
.post(controller.login);
router.route('/logout')
.post(verifyToken, controller.logout);
router.route('/get_user_data')
.get(verifyToken, controller.get_user_data);
return router;
}
And of course my index file(index.js):
let users = require('./v1/routes/users.js');
app.users('/api/v1/users', users(router));
However, when I try to get a POST Request of type http://localhost:3000/api/v1/users in Postman, the result is Cannot POST /api/v1/users
What is the reason for that?
You need to add /api/v1/ in any route
like this
module.exports = (router) => {
router.route('/api/v1/list_users')
.get(verifyToken, controller.list_users);
router.route('/api/v1/register')
.post(controller.register);
router.route('/api/v1/login')
.post(controller.login);
router.route('/api/v1/logout')
.post(verifyToken, controller.logout);
router.route('/api/v1/get_user_data')
.get(verifyToken, controller.get_user_data);
return router;
}
and other thing , you need to create route for users.
I created a simple restful api with express in github you can see any example for create a route in this repository
Github Repo

Problem in this email verification approach?

Edit: [how to handle case of jwt expiration ]
I have read some article on how to implement email verification for your web application and each one follow up:
Creating a unique string, saving it in db with reference to user being verified and sending that unique string as a link for verification. When user visits that link, unique string is run against db and refernced user is validated.
But, I tried it in a different way, that user model contains verify status and will be false by default and when new user sign_up then a jwt token is created and that is sent to user as verification link and when the link is visited, jwt token is verified and user verify status is changed to true.
Above implementation worked for me and removes the use of creating and storing token in separate db but I am afraid this approach might have problems which I might not be aware of. here's the code for above.
passport configuration for auth(config-passport.js)
const bcrypt = require('bcrypt')
const LocalStrategy = require('passport-local').Strategy
const { User } = require('./models/user');
module.exports = (passport) => {
// passport local strategy
const authUser = (email, password, done) => {
User.findOne({ email: email }, function(err, user){
if(err) return done(err);
if(!user || !user.verify) return done(null, false);
if(user.verify){
bcrypt.compare(password, user.password, (err, isValid) => {
if (err) {
return done(err)
}
if (!isValid) {
return done(null, false)
}
return done(null, user)
})
}
})
}
passport.serializeUser((user, done) => {
done(null, user.id)
});
passport.deserializeUser((id, done) => {
User.findOne({ _id: id }, function(err, user){
done(err, user)
});
});
passport.use(new LocalStrategy({
usernameField: 'email'
}, authUser));
}
user model
'use strict';
const mongoose = require('mongoose');
const bcrypt = require('bcrypt')
const Joi = require('joi');
const Schema = mongoose.Schema;
//any changes done to userSchema will need changes done to userValidation.js
const userSchema = new Schema({
username: {type: String, required: true, maxlength: 100},
email: {type: String, unique: true, lowercase: true, required: true},
mobile: {type: Number, unique: true, required: true},
password: {type: String, required: true},
verify: { type: Boolean, enum: [false, true], default: false },
lib: [{ type: Schema.Types.ObjectId, ref: 'Book' }],
book_id: [{ type: Schema.Types.ObjectId, ref: 'Book' }]
});
const JoiValidUser = Joi.object({
username: Joi.string().min(3).max(50).required(),
email: Joi.string().email().min(5).max(50).required(),
mobile: Joi.string().regex(/^[0-9]{10}$/).required().messages({ 'string.pattern.base': `Phone number must have 10 digits.` }),
password: Joi.string().min(5).max(255).required()
});
userSchema.pre('save', async function(next){
const user = this;
const hash = await bcrypt.hash(user.password, 10);
this.password = hash;
next();
})
userSchema.methods.isValidPassword = async function(password) {
const user = this;
const compare = await bcrypt.compare(password, user.password);
return compare;
}
const User = mongoose.model('User', userSchema);
module.exports = { User, JoiValidUser };
user creation controller(userCreate.js)
const { User, JoiValidUser } = require('../models/user');
const mailer = require('../controller/mailHandler')
//takes data posted and form it in a readable format
//then validate/sanitize it against schema
//if error arises or user already exists a msg is passed on
//else user creation process is executed
module.exports = async function(req, res){
let user = {
username: req.body.username,
email: req.body.email,
mobile: req.body.mobile,
password: req.body.password
}
try{
JoiValidUser.validate(user);
const ExistUser = await User.findOne({
$or: [
{ email: req.body.email },
{ mobile: req.body.mobile }
]
});
if(ExistUser)
throw new Error("Email/Mobile Number already Registered");
await (new User(user)).save();
mailer(user.username, user.email);
res.send({ msg: "A Verification link is sent to mail" });
} catch(err) {
res.render('error', { message: err.message })
}
}
user verification route (verify.js)
const router = require('express').Router();
const jwt = require('jsonwebtoken');
const config = require('dotenv').config().parsed
const { User } = require('../models/user')
const routePlan = require('../route_plan');
router.get('/:token', async(req, res) => {
const { email } = jwt.verify(req.params.token, config.SECRET);
await User.findOneAndUpdate({ email: email }, {
$set: { verify: true }
});
res.send("Welcome ...")
})
module.exports = router;
EDIT:
Thank you all for your feedback but there is another problem I want to be clear of on how to handle case when jwt token expires because link will be invalid and user cannot try to sign up again because his info is already in db and he cannot register again

Getting error data and salt arguments required on bcrypt?

I am trying to save a user to MongoDB as follows, but I am getting the error bcrypt Error: data and hash arguments required. I have checked the same error question asked by other Dev on StackOverflow but it doesn't help. I have attached the codes of the model file and router file.
User Model file
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const uSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
min: 4,
max: 30
},
email: {
type: String,
required: true,
trim: true,
unique: true,
index: true
},
hash_password: {
type: String,
required: true,
min: 6,
max: 12
},
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'admin'
}
}, { timestamps: true });
uSchema.virtual('password')
.set(function (password) {
this.hash_password = bcrypt.hashSync(password, 10);
});
uSchema.methods = {
authenticate: function (password) {
return bcrypt.compareSync(password, this.hash_password);
}
}
module.exports = mongoose.model('User', uSchema);
User Router file
const express = require('express');
const router = express.Router();
const User = require('../models/user.model');
router.post('/login', (req, res) => {
});
router.post('/signin', (req, res) => {
User.findOne({ email: req.body.email })
.exec((error, user) => {
if (user) return res.status(400).json({
message: 'User already exists.'
});
const {
fullName,
email,
password
} = req.body;
const _user = new User({
fullName,
email,
password
});
_user.save((error, data) => {
if (error) {
return res.status(400).json({
message: 'Something went wrong'
});
} if (data) {
return res.status(201).json({
user: data
})
}
})
});
});
module.exports = router;
You can do it in the router file instead.
const bcrypt = require("bcrypt")
// ...
router.post('/signin', (req, res) => { // Change this to signup
User.findOne({ email: req.body.email })
.exec((error, user) => {
if (user) return res.status(400).json({
message: 'User already exists.'
});
const {
fullName,
email,
password
} = req.body;
const hashedPassword = bcrypt.hashSync(password, 10);
const _user = new User({
fullName,
email,
hashedPassword
});
_user.save((error, data) => {
if (error) {
return res.status(400).json({
message: 'Something went wrong'
});
} if (data) {
return res.status(201).json({
user: data
})
}
})
});
});
module.exports = router;
and delete the password virtual from the model.

Unable to reference from token schema token mongodb express passport

i need help with this problem been dealing it for days.
i am trying to make a verification email route by using passport to hash passwords while issuing a verification token to the user.
here is my code for index.js in controllers folder
const User = require("../models/user");
const Token = require("../models/token")
const crypto = require("crypto");
const nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: process.env.GMAILUSER,
pass: process.env.GMAILPW
}
});
module.exports = {
async postRegister(req, res, next) {
var user = new User({
name: req.body.name,
email: req.body.email,
isVerified: false,
username: req.body.username
});
await User.register(user, req.body.password);
res.redirect('/');
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message
});
}
var mailOptions = {
to: user.email,
from: 'xxxt#xxx.com',
subject: 'xxxxx verify email',
text:'You are receiving this because you need to verify your email for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/confirmation/' + token.token + '\n\n' +
'If you did not request this, please ignore this email.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send('A verification email has been sent to ' + user.email + '.');
});
})
},
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("sss")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
if (!user) return console.log(user)
if (user.isVerified) return res.status(400).send({ type: 'already-verified', msg: 'This user has already been verified.' });
user.isVerified = true;
user.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send("The account has been verified. Please log in.");
})
});
};
})
}
}
This is my Token Schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const tokenSchema = new mongoose.Schema({
_userId: {
type: Schema.Types.ObjectId,
ref: 'User' },
token: {
type: String,
required: true },
createdAt: {
type: Date, required: true,
default: Date.now, expires: 43200 }
});
tokenSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('Token', tokenSchema);
lastly my user schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: String,
name: String,
email: { type: String, unique: true },
image: String,
isVerified: { type: Boolean, default: false },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post'
}
]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
everything works fine until the part where the email verification was sent to my email and when i clicked on the link. It gives an error, i tried to console.log
and found that this line from controllers folder index.js
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("err")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
gives me back null.
how do i link that current line to get the token from the registered user?
i've used postman to send a get request to the confirmation route while giving it back the same token and it works.

Resources