Node.js "cannot set property firstName of undefined" - mysql

I am following this tutorial,
https://www.youtube.com/watch?v=OnuC3VtEQks
to create a login system for my node app, only thing is since he is using mongoDb and I'm using mySql, I had to think of a way around him setting up his mongoose schema (right around 7:14 of the video), so I just exported a user module and assigned properties to it in my create a user logic.
Here's the user module
//user.js in models
var bcrypt = require('bcryptjs');
var User = {
firstName: "firstName",
lastName: "lastName",
email: "email",
username: "username",
password: "password",
}
module.exports = User;
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);
});
});
}
Here is my route for the registration form
// main.js in router folder
var user = require('../models/user').User;
router.post('/register', function(req, res){
var firstName = req.body.firstName;
var lastName = req.body.lastName;
var email = req.body.email;
var username = req.body.username;
var password = req.body.password;
var cpassword = req.body.cpassword;
//Validation
// req.checkBody('firstName', 'First name is required!').notEmpty();
// req.checkBody('lastName', 'Last name is required!').notEmpty();
// req.checkBody('email', 'E-mail is invalid!').isEmail();
// req.checkBody('username', 'Username is required!').notEmpty();
// req.checkBody('password', 'Password is required!').notEmpty();
// req.checkBody('cpassword', 'Passwords do not match!').equals(password);
var errors = req.validationErrors();
if(errors){
res.render('register', {
errors: errors
});
console.log(errors);
} else {
var newUser = user;
newUser.firstName = firstName;
newUser.lastName = lastName;
newUser.email = email;
newUser.username = username;
newUser.password = password;
User.createUser(newUser, function(err, user){
if(err) throw err;
console.log(user);
});
req.flash('success_msg', 'You are now registered!');
res.redirect('login');
}
});
Now I keep getting "cannot set property firstName of undefined error", but I thought I had access to my User object from the user.js module? From what I can tell it looks like he's just using the form input to instantiate the user object and create it with some password hashing.
If this is a bad approach, I am completely open to any ideas to make this simpler. I am completely new to node and my senior project is due in 2 weeks, so any help will be immensely appreciated and rewarded with upvotes and internet credits :)

You should change your user.js as follows:
//user.js in models
var bcrypt = require('bcryptjs');
var User = {
firstName: "firstName",
lastName: "lastName",
email: "email",
username: "username",
password: "password",
}
module.exports = {
User: User,
createUser: function (newUser, callback) {
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(newUser.password, salt, function (err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
}
This would solve all of your problems.

You declare (and export) the object User (with a capital U) earlier in the program, but later on try and access the variable user, which is undefined
var newUser = user;

Related

React NodeJS mySQL Can't login with password after Hashing

I've created a salt and a hash function to hash my password.
I'm trying to login with the original password and it won't let me, but if I try to log in with the hashed password from the database it will give me to log in.
const salt = "HashedPasswordCheck";
hash function:
function has(plainText: string):string{
if(!plainText) return null;
const hashedText = crypto.createHmac("sha512", salt).update(plainText).digest("hex");
return hashedText;
}
auth-logic.ts:
async function login(credentials:CredentialsModel):Promise<string>{
const error = credentials.validate();
if(error) throw new ValidationErrorModel(error);
const sql = `SELECT * FROM users WHERE username = ? AND password = ?`;
const users = await dal.execute(sql, [credentials.username, credentials.password]);
credentials.password = cyber.hash(credentials.password);
if (users.length === 0) throw new UnauthorizedErrorModel("Incorrect username or
password");
const user = users[0];
const token = cyber.getNewToken(user);
return token;
}
I tried to more the has before sending the query and still not working.
I checked this before and it was the same and worked but on this new project i'm working on it's not working properly.
credentials-model:
class CredentialsModel {
public username: string;
public password: string;
public constructor(credentials: CredentialsModel) {
this.username = credentials.username;
this.password = credentials.password;
}
public static validationSchema = Joi.object({
username: Joi.string().required().min(4).max(20),
password: Joi.string().required().min(4).max(50)
});
public validate(): string {
const result = CredentialsModel.validationSchema.validate(this);
return result.error?.message;
}
}
export default CredentialsModel;
auth-controller.ts:
// http://localhost:3001/api/auth/login
router.post("/auth/login", async (request: Request, response: Response, next:
NextFunction) => {
try {
const credentials = new CredentialsModel(request.body);
const token = await authLogic.login(credentials);
response.json(token);
}
catch(err:any){
next(err);
}
});
I didn't add a React code because it's a back end problem..
Thank you for any one that can help!
Found the issue.
I had 24 chars in my mySQL db, so it saved only part of the string and that's why I had an issue.
Solved by increasing the varchar amount on mySQL.

Bycrpt cannot compare and always sends null values

i m using Bycrpty library for security. so i read bycrpt Official document.
i sent postman in signup routes. it work or not
it was success full! like that.
so i have to compare the passwords When logging in,
but compare is always failed. it's my code..
const jwt = require('jsonwebtoken');
// const { Op } = require("sequelize");
const { user } = require("../../models");
const bcrypt = require("bcrypt");
const salt = bcrypt.genSaltSync(10) ;
signUpController: async (req, res) => {
const { username, email, password} = req.body;
if( !(username && email && password) ){
res.status(405).send({
"message" : "invalid request"
});
}
else{
const userInfo = await user.findOne({
where: {
email: email,
username : username
}
});
if(userInfo === null){
const newUser = await user.create({
username: username,
email : email,
password: bcrypt.hashSync(password, salt),
});
let response = {
username: newUser.username,
email: newUser.email,
username: newUser.username,
password : newUser.password
}
res.status(201).json( response );
}
else{
res.status(409).send({
"message" : "email already exist"
});
}
}
},
login : async(req,res)=>{
const { email, password } = req.body;
const userInfo = await user.findOne({
where: {
email: email,
password : password
}
});
// console.log("req: ", req)
if(!userInfo) {
await res.status(400).send({data : null, message : 'not authorized'})
}
else {
const data = {...userInfo.dataValues}
console.log('password:', checkMail.password)
bcrypt.compareSync(password, userInfo.password) ;
delete data.password
const accessToken = jwt.sign(data, process.env.ACCESS_SECRET, {expiresIn : '3h'}) // create jwt
const refreshToken = jwt.sign(data, process.env.REFRESH_SECRET, {expiresIn : '1h'}) // save in cookie .
res.cookie("refreshToken", refreshToken)
res.status(200).send({data:{"accessToken": accessToken}, message:'ok'})
}
}
What should I do to be successful? I need advice and tips.
I'm slightly confused as your using async/await for some things like the database library however not for bcrypt which also has promises and instead you're using their sync versions. As a first advice I wouldn't use the sync versions of the code as they block the eventLoop.
There is another problem with your logic - which is highlighted below.
const jwt = require('jsonwebtoken');
// const { Op } = require("sequelize");
const { user } = require("../../models");
const bcrypt = require("bcrypt");
const salt = bcrypt.genSaltSync(10) ;
signUpController: async (req, res) => {
const { username, email, password} = req.body;
if( !(username && email && password) ){
res.status(405).send({
"message" : "invalid request"
});
}
else{
const userInfo = await user.findOne({
where: {
email: email,
username : username
}
});
// using email/username as unique fields to find a user and check if they already have an account
if(userInfo === null){
const newUser = await user.create({
username: username,
email : email,
password: bcrypt.hashSync(password, salt),
// saving the hashed password rather than the plaintext password
});
let response = {
username: newUser.username,
email: newUser.email,
username: newUser.username,
password : newUser.password
}
// do not under any circumstance send the password back to the user.
res.status(201).json( response );
}
else{
res.status(409).send({
"message" : "email already exist"
});
}
}
},
login : async(req,res)=>{
const { email, password } = req.body;
// you're trying to find a user that exists based on their email and plaintext password, but the password you've saved is the HASHED version not the plaintext version so this result will always be empty... No such user exists
const userInfo = await user.findOne({
where: {
email: email,
password : password
}
});
// console.log("req: ", req)
if(!userInfo) {
// hence this error is present ALL THE TIME
await res.status(400).send({data : null, message : 'not authorized'})
}
else {
const data = {...userInfo.dataValues}
console.log('password:', checkMail.password)
bcrypt.compareSync(password, userInfo.password) ;
// you wouldn't need this step as you've found the user based on the password
delete data.password
const accessToken = jwt.sign(data, process.env.ACCESS_SECRET, {expiresIn : '3h'}) // create jwt
const refreshToken = jwt.sign(data, process.env.REFRESH_SECRET, {expiresIn : '1h'}) // save in cookie .
res.cookie("refreshToken", refreshToken)
res.status(200).send({data:{"accessToken": accessToken}, message:'ok'})
}
}
This seems to me rather than misunderstanding how password hashing works you don't understand the data in your database.
I'd suggest to get a visual database explorer for whatever database you're trying to use. There are many free and opensource ones out there!

Strange nodejs behaviour when logging in a user

The problem is that it shows that it is successfully logged in (201) without the redirect code, but with it, it shows a 302 error and the email_address is undefined.
What could be the problem here? I still can't come to a conclusion.
The problem may be in the order of the code I guess?
const login = async (req, res, next) => {
const { email_address, password, user_email, user_password}: { email_address: string, password: string, user_email: string, user_password: string } = req.body;
try {
const userWithDetails = 'SELECT * FROM users WHERE email_address = user_email AND password = user_password'; //w form info
if (userWithDetails) {
req.session.loggedin = true; //true
req.session.email_address = email_address; //undefined
console.log(req.session.email_address)
// return res.redirect('./index.html')
}
res.status(201).send('Succesfully signed in');
// res.status(403).send('Password is not correct');
} catch(error) {
res.status(404).send(`User with email ${email_address} not found!`);
}
await next;
};
NEW CODE ***
const login = async (req, res, next) => {
const { email_address, password}: { email_address: string, password: string} = req.body;
const userWithDetails = 'SELECT * FROM users WHERE email_address = ?';
return con.query(userWithDetails, email_address, (err, results) => {
if (err) {
console.error(err);
}
const user = results.find(emailObj => emailObj.email_address === email_address);
if (results && results.length && user.email_address) {
req.session.loggedin = true;
req.session.email_address = email_address;
const matchPassword: boolean = bcrypt.compareSync(password, user.password);
if (matchPassword) {
const token = jwt.sign({ user }, 'aaaa', { expiresIn: '1h'});
res.status(200).send({message: 'Logged in', token: token});
} else {
res.status(403).send('Password is not correct');
}
} else {
res.status(404).send(`User with email ${email_address} not found!`);
}
});
await next;
}
You don't execute your sql query at any point.
You just say :
query = 'select blabla'
if(query){...}
Of course this will always be true. You want to run the query on your database.
Also in your query you don't properly use the variables, see string formatting :
let my_var = `SELECT xxx from xxx where username = '${username}'`
Also please sanitize the parameters to prevent SQL Injection...

passport authentication callback not calling passport middleware

I am trying to test if a GET request to a specific route is protected by using JSON Web Tokens with passport JwtStrategy; however, it seems like the passport.use middleware function I have in my server.js file is not executing. The console.log I have in there never shows up in my shell/terminal. My login route works, but not the profile route. I am using postman and I entered http://localhost:3000/profile for the GET method and in the Headers tab I chose Authorization for Key and for the Value I copied and pasted the long JSON web token string, but it keeps saying unauthorized. That is because my passport.use function is never getting executed.
//Server.js file
var JwtStrategy = require("passport-jwt").Strategy;
var ExtractJwt = require("passport-jwt").ExtractJwt;
var User = require("../models/user");
var config = require('./secret');
app.use(passport.initialize());
app.use(passport.session());
let options = {};
//pass token back and forth
options.jwtFromRequest = ExtractJwt.fromAuthHeader();
options.secretOrKey = config;
passport.use(new JwtStrategy(options, (jwt_payload, done) => {
*******************************************
//this console log doesn't show up in shell which makes be believe its never getting here
*******************************************
console.log("JWT PAYLOAD", jwt_payload)
User.getUserById(jwt_payload._id, (err, user) => {
if(err){
return done(err, false);
}
if(user){ //null for error
return done(null, user);
}else{
return done(null, false);
}
});
}));
//Routes file where the passport.authenticate callback is called
var passport = require('passport');
var jwt = require('jsonwebtoken');
var secret = require('../config/secret')
var User = require('../models/user');
router.post('/login', (req, res) => {
var username = req.body.username;
var password = req.body.password;
console.log("SECRET2", secret);
console.log("SECRET", secret.secret);
User.getUserByUsername(username, (err, user) => {
if(err){
throw err;
}
if(!user){
return res.json({ success: false, msg: "User not found"});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if(err){
throw err;
}
if(isMatch){
var token = jwt.sign(user, secret.secret, {
expiresIn: 604800 //1 week in seconds, token expires and requires to log back in
});
console.log('TOKEN IN LOGIN ROUTE', token)
res.json({
//tokens are then stored in local storage or cookie
success: true,
token: 'JWT ' + token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
}else{
return res.json({ success: false, msg: "Incorrect password"});
}
});
});
});
router.get('/profile', passport.authenticate('jwt', {session:false}), (req, res) => {
res.json({user: req.user});
});
//User model
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: {
type: String,
trim: true,
required: "First Name is Required"
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
var User = mongoose.model("User", UserSchema);
module.exports = User;
//Alternate syntax for exporting Schema
// var User = module.exports = mongoose.model("User", UserSchema);
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback){
var query = { username: username }
User.findOne(query, callback);
}
//Custom User model function that will take in the newUser object and hash the password.
module.exports.addUser = function(newUser, callback){
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err){
throw err
}
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.comparePassword = function(password, hash, callback){
bcrypt.compare(password, hash, (err, isMatch) => {
if(err){
throw err
}
callback(null, isMatch);
});
}
Update:
I tried putting a space after 'JWT' in for the value for Authorization in the postman, but it still does not work and the console log is not showing. Is it because I am somehow not exporting or linking the passport.use I have defined in my server.js to my GET '/profile' route in my routes file?
Update 2: Added model and Login route
Maybe it is necessary to include more code to see your issue, but it seems like the strategy is not being exported correctly. When you create a new strategy, you can include an 'alias' to use it in the entry point:
passport.use('local-login', new JwtStrategy(options, (jwt_payload, done) => {
....
}
router.get('/profile', passport.authenticate('local-login', {session:false}), (req, res) => {
res.json({user: req.user});
});

node.js , mongodb, mongoose, html how can i insert data(in index.html)?

var mongoose = require('mongoose')
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/mydatabase'); //connect database
/* *
* * Define UserSchema
* **/
var UserSchema = new Schema({
user_id : String,
email : String,
base_location : Number,
user_type : String,
number_of_event : Number
});
mongoose.model('User', UserSchema);
var User = mongoose.model('User');
var user =new User
app.post('/api/users', function (req, res){
var product;
console.log("User: ");
console.log(req.body);
user = new ProductModel({
user_id: req.body.user_id,
email: req.body.email,
base_location: req.body.base_location,
});
product.save(function (err) {
if (!err) {
return console.log("created");
} else {
return console.log(err);
}
});
return res.send(user);
});
this is my app.js
it contains schema and post function
i don't know how can i use this file in html
i want to make indext.html which can insert user data
how can i do that?
3421
423
There are a multitude a possibilities. What I usually do is create an express server and attach the routes to special functions in express.
//your modules
var express = require('express'),
app = express(),
mongoose = require('mongoose');
//connect mongo
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/mydatabase');
//schema
var UserSchema = new Schema({
user_id : String,
email : String,
base_location : Number,
user_type : String,
number_of_event : Number
});
mongoose.model('User', UserSchema);
var User = mongoose.model('User');
var user =new User
app.post('/api/users', function (req, res){
//do your stuff
});
app.listen(80);
You then will need to run the above script (lets call it app.js) with
node app.js
If the code above is sane, this will run the server. When you connect to the server with the app then you will receive a connection. You should also look up some docs on socketIO.