ReferenceError: nome is not defined - node.js

I'm trying to make a function retrieve a callback. First the error was that callback parameter was not a function, than I tried to fix my syntax its saying that a parameter is not defined.
controller: (ERROR AT LINE 10):
//Tried to declare like "function registraU(nome, ... ())
const registraUsuario = (nome, email, password, (e, usuarioCriado) => {
UsuarioModel.findOne({ email: email }, (e, match) => {
if (e) { return callback(e); }
if (match !== null) {
return callback(null, null);
} else {
var hash = bcrypt.hashSync(password, 10);
password = hash;
novoUsuario = {
nome: nome,
email: email,
password: password
}
var temp = new UsuarioModel(novoUsuario);
temp.save(function(e, usuarioCriado){
if(e){console.log(e)};
return callback(null, usuarioCriado);
});
}
});
});
And this is the code that is calling it:
passport.use('local-registro', new LocalStrategy({
nomeField: 'nome',
emailField: 'email',
passwordField: 'password',
passReqToCallback : true
},
(req, nome, email, password, done) => {
UsuarioController.registraUsuario(nome, email, password, (e, callback) => {
if(e) {return done(e); }
if(!novoUsuario){
return done(null, false, req.flash({"erroRegistro": "Email já cadastrado"}));
} else {
return done(null, novoUsuario);
}
});
}
));

You're declaring as a kind of es6 arrow function but you forgot to add the function body...
const registraUsuario = (nome, email, password, (e, usuarioCriado) => {...});
But where is the rest of you function body ??
const registraUsuario = (nome, email, password, (e, usuarioCriado) => {...}) => {
// your function body
}
That's why you code is broken... It's not interpreted as a function declaration.

Related

Mongoose schema async method doesn't wait

this is my code :
const user = await User.findOne({ email }).select('+password');
console.log(' user value : ', user);
const boolean = await user.comparePassword(password, user.password);
console.log('boolean value : ', boolean);
The results of my flags in console :
user value : {
role: 'user',
passwordChangedAt: 2021-04-14T02:19:54.689Z,
_id: 6074b03b28f33810a528d61f,
name: 'Javier',
password: '$2a$12$cSsURwyoGGd2j9kreuwnGur3pYaTmnY3K2vjXRSJpDhptDwy0t4lG',
__v: 0
}
boolean value : undefined
So, boolean var is not waiting, and i don't know why. These is my function in my userschema :
userSchema.methods.comparePassword = catchAsync(async function comparePassword(
password,
userPassword
) {
const aux = await bcrypt.compare(password, userPassword);
return aux;
});
```
How can i force boolean to wait for the result of comparePassword function ?
userSchema.methods.comparePassword = async (password, userPassword) => {
return new Promise((resolve, reject) => {
bcrypt.compare(password, userPassword, (err, result) => {
if (err) {
resolve(false);
}
if (result == true) {
resolve(true);
} else {
resolve(false);
}
})
});
}

How can I improve error handling on a pool

I am have created a class User that will hold the logic for inserting a new user into a postresql database. My code works perfectly but i think it is poorly written and would like some views on how to improve it, especially error handling.
const pool = require('../config/config.js');
// user constructor
class User {
constructor(user) {
this.username = user.username;
this.email = user.email;
this.password = user.password;
this.role = user.role;
}
// save new user in databes
createUser(res) {
pool.connect((err, client, done) => {
done();
if (err) return res.status(400).json({ err });
client.query('INSERT INTO users (username, email, password, role) VALUES($1, $2, $3, $4)', [this.username, this.email, this.password, this.role], (error) => {
if (error) return res.json({ error });
return res.json({ message: 'created successfully' });
});
});
}
}
module.exports = User;
app.post('/', (req, res) => {
const user = new User({
username: 'Femoz',
password: '1234',
role: 'admin',
email: 'femoz#gmail.com',
});
user.createUser(res);
// res.json('user created successfully');
});
const pool = require('../config/config.js');
class User {
constructor(user) {
this.username = user.username;
this.email = user.email;
this.password = user.password;
this.role = user.role;
}
// save new user in databes
save(cb) {
const user = this;
// Perhaps, you should to do some checks of props e.g. a name is not empty
if (!user.name)
return cb(Error('Empty name'));
// I think that you should implement some wrapper
// e.g. `db.run` to avoid call the pool directly each time.
pool.connect((err, client, done) => {
// done(); here is an error. You released the db-connection too early.
if (err)
return cb(err);
// I assumed that the result of done() is undefined so cb will be called.
// Overwise use `&&` instead `||`.
client.query(
'INSERT INTO users (username, email, password, role) VALUES($1, $2, $3, $4) RETURNING id',
[user.username, user.email, user.password, user.role],
(err, res) => done() || cb(err, id: res && res.rows[0].id)
);
});
}
}
module.exports = User;
app.post('/', (req, res, next) => {
const user = new User({
username: 'Femoz',
password: '1234',
role: 'admin',
email: 'femoz#gmail.com',
});
// Return new id is better than a static text :)
user.save((err, id) => err ? res.status(400).json({error: err.message}) : res.json({id}));
// OR
// For Express you can pass error to an error handler
user.save((err, id) => err ? next(err) : res.json({id}));
});

How to fix "TypeError: cb is not a function" error for comparing passwords

I've been starting to add user authentication into my app and when adding the login route, i've been getting the error "TypeError: cb is not a function". I know it is coming from my login route as all my other routes work fine.
I have tried researching the issue and trying a few fixes that i've found but none have worked. So i'm starting to believe i've messed up somewhere and I can't find where.
Login Route:
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username }, function (err, user) {
if (err || user == null) {
console.log(err);
res.redirect('/login');
}
if (!user.comparePassword(req.body.password)) {
req.flash('invalidDetails', 'Wrong username or password!');
res.redirect('/login');
} else {
req.session.userId = user._id;
req.flash('loggedIn', 'User ' + req.body.username + ' has been logged in!');
return res.redirect('/');
}
});
});
User Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
UserSchema.pre('save', function (next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm expecting it to compare the password with the password that has been entered into the login form with the password that is stored in the database and log in the user if the password is correct or redirect back to the login page with the invalidDetails flash message if the password is incorrect.
But what i'm actually getting is the "TypeError: cb is not a function" error when trying to login.
You are not passing a callback that's why.
If you want a promise based method you can write something like this
AuthSchema.methods.comparePassword = function(candidatePassword) {
const currentPassword = this.password;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, currentPassword, function(err, isMatch) {
if (err) return reject(err);
resolve(isMatch);
});
})
};
And now you can call that with await

Express-validation not producing the correct errors

So, this worked before, and all the sudden decided to stop working, and I have no idea why.
EDIT: Updated the code to show what I currently got now
router.post('/register', async (req, res) => {
// let query
let query;
// Start email checks
req.check('email', 'Email is not valid.')
.isEmail()
.custom(async value => {
query = {email: value};
User.findOne(query).then(user => {
if (user) return false;
});
}).withMessage('Email is in use.');
// Start username checks
req.check('username', 'Username is required.')
.notEmpty()
.isLength({ min: 5, max: 15}).withMessage('Username requires 5-15 alphanumberic characters.')
.isAlphanumeric().withMessage('Username must be alphanumeric only.')
.custom(async value => {
query = {username: value}
User.findOne(query).then(user => {
if (user) return false;
});
}).withMessage('Username is in use.');
// Start password checks
req.check('password', 'Password is required.')
.notEmpty()
.isLength({min: 5}).withMessage('Password must be atleast 5 characters long.');
req.check('confirmPassword', 'Confirm Password is required.')
.notEmpty()
.custom(value => value === req.body.password).withMessage('Password must match');
const errors = await req.getValidationResult();
//console.log(errors);
if (!errors.isEmpty()) {
res.render('index', {
errors: errors.mapped()
});
} else {
let newUser = new User({
email: req.body.email,
username: req.body.username,
password: req.body.password,
});
let hash = bcrypt.hashSync(req.body.password, 10);
newUser.password = hash;
newUser.save(err => {
if (err) {
console.log(err);
} else {
res.render('index', {
success: 'Registration Successful'
});
}
});
}
});
So its pretty clear its something with my custom checks, and I do not know why.
EDIT:
It seems there is confusion. The checks are working correctly, what I'm having issues with is it populating the errors when I want it to. If i try to register with the same email, it will pull up the user and will go through my if statements. If I use Promise.reject() it doesn't work. If I use false, it doesn't work. Again, the checks itself work, the error handling seems like it isn't.
EDIT TWO:
So I have tried this method (all the other code is still the same)
// Start email checks
req.checkBody('email', 'Email is not valid.')
.isEmail()
.custom(value => {
query = {email: value}
User.findOne(query).then(user => {
if (user) console.log('Email Exists'); return false;
});
}).withMessage('Email in use.');
// Start username checks
req.check('username', 'Username is required.')
.notEmpty()
.isLength({ min: 5, max: 15}).withMessage('Username requires 5-15 alphanumberic characters.')
.isAlphanumeric().withMessage('Username must be alphanumeric only.')
.custom(value => {
query = {username: value}
User.findOne(query).then(user => {
if (user) console.log('Username Exists'); return false;
});
}).withMessage('Username in use.');
This should work. Since node.js is non render blocking,the db query may not complete before it proceeds to next step. You could use the format I have posted below or try the async library in which case await keyword should be placed before User.findOne
User.findOne(query).then(user=>{
if(user) return false
}).catch(err => console.log(err))
Finally found an answer, which incidentally makes it a little easier to read as well. I ended up making my own custom validators:
customValidators: {
emailExists: (email) => {
let query = {email: email};
return new Promise((resolve, reject) => {
User.findOne(query, (err, results) => {
if (results === null) {
resolve(err);
}
reject(results);
});
});
},
userNameExists: (username) => {
let query = {username: username};
return new Promise((resolve, reject) => {
User.findOne(query, (err, results) => {
if (results === null) {
resolve(err);
}
reject(results);
});
});
}
},
Then:
req.check('email', 'This email is in use.').emailExists();
req.check('username', 'Username is in use.').userNameExists();
req.asyncValidationErrors().then(() => {
console.log('No errors');
let newUser = new User({
email: req.body.email,
username: req.body.username,
password: req.body.password,
});
let hash = bcrypt.hashSync(req.body.password, 10);
newUser.password = hash;
newUser.save(err => {
if (err) {
console.log(err);
} else {
res.render('index', {
success: 'Registration Successful'
});
}
});
}).catch(errors => {
res.render('index', {
errors: errors
});
});

passport js deserialize user, variable undefined

I'm trying to build a backend for a web app, but I'm stuck on passport. Right now I can register a user and log that user in, but I can not log in twice in a row. Whenever deserialize user is called I get "TypeError: Cannot read property 'findOne' of undefined" I see that user is undefined but it is defined when I call it on login() and signup()
var login = require('./login');
var signup = require('./signup');
module.exports = function (passport: any, user: any) {
// Passport needs to be able to serialize and deserialize users to support persistent login sessions
passport.serializeUser(function (user: any, done: any) {
done(undefined, user.username);
});
// deserialize user
passport.deserializeUser(function (id: any, done: any) {
user.findOne(id).then(function (user: any) {
if (user) {
done(null, user.get());
} else {
done("error", undefined);
}
});
});
// Setting up Passport Strategies for Login and SignUp/Registration
login(passport, user);
signup(passport, user);
}
in my app.ts file i implement the strategies like this:
var models = require("./config/models/User");
require('./config/passport/init')(passport, models.user);
signup.js
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport : any, user : any) {
var User = user
passport.use('signup', new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
}, function(req: Request, username: string, password: string, done: any) {
var User = user
console.log("signup"+User)
var generateHash = function(password: string) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
username: username
}
}).then(function(user: any) {
if (user)
{
console.log('already taken')
return done(null, false, {
message: 'That email is already taken'
});
} else {
var userPassword = generateHash(password);
var data =
{
username: username,
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname
};
User.create(data).then(function(newUser: any, created: boolean) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
}
));
}
login.js
import { passport } from '../../app'
var LocalStrategy = require('passport-local').Strategy;
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport : any, user : any) {
//LOCAL SIGNIN
passport.use('login', new LocalStrategy(
{
// by default, local strategy uses username and password, we will override with email
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req : Request, username : string, password : string, done : any) {
var User = user;
console.log("login: "+User)
var isValidPassword = function(userpass : string, password : string) {
return bCrypt.compareSync(password, userpass);
}
User.findOne({
where: {
username: username
}
}).then(function(user : any) {
if (!user) {
return done(null, false, {
message: 'User does not exist'
});
}
if (!isValidPassword(user.password, password)) {
return done(null, false, {
message: 'Incorrect password.'
});
}
var userinfo = user.get();
return done(null, userinfo);
}).catch(function(err : Error) {
console.log("Error:", err);
return done(null, false, {
message: 'Something went wrong with your Signin'
});
});
}
));
}
I solved it myself,
I imported the model like so:
var models = require("../models/User");
then I changed the findOne call like so:
models.user.findOne({ where: {username: id} }).then(function (user: any) {
if (user) {
done(null, user.get());
} else {
done("error", undefined);
}
});

Resources