validator isStrongPassword is not a function - node.js

I get this error whenever I try to register a user!
isStrongPassword is not a function
I tried typing the isStrongPassword which is Min 8 characters, 1 uppercase, 1 lowercase, 1 number, 1 symbol but no luck, I don't know what i'm missing !!
Auth.js
const isEmail = require ("validator/lib/isEmail.js")
const isStrongPassword = ("validator/lib/isStrongPassword");
//REGISTER
router.post("/register", async (req, res, next) => {
try {
if (!req.body.username) {
return next(createError(400, "Invalid username"));
} if (!req.body.email || !isEmail(req.body.email)) {
return next(createError(400, "Invalid email"));
} if (!req.body.password) {
return next(createError(400, "Invalid password"));
} else if (!isStrongPassword(req.body.password)) {
return next(
createError(
400,
"Invalid password: Min 8 characters, 1 uppercase, 1 lowercase, 1 number, 1 symbol"
)
);
} else {
const checkUsername = await User.findOne({ username: req.body.username });
const checkEmail = await User.findOne({ email: req.body.email });
if (checkUsername) {
return next(createError(400, "Username taken"));
} else if (checkEmail) {
return next(createError(400, "Email taken"));
} else {
//generate new password
const salt = await bcrypt.genSalt(10);
const hashedPass = await bcrypt.hash(req.body.password, salt);
//create new user
const newUser = new User ({
username: req.body.username,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPass,
repeatPassword: hashedPass,
birthday: req.body.birthday,
});
//save user and respond
const user = await newUser.save();
res.status(200).json({
message: "Successfully registered",
user,
});
}
}
} catch(err) {
next(err);
}
});

You are missing a require statement from your isStrongPassword import - and also presumably the file extension. e.g.
const isStrongPassword = ("validator/lib/isStrongPassword");
Should presumably be
const isStrongPassword = require ("validator/lib/isStrongPassword.js");

Related

Authentication Error (nodejs, mongodb, jwt)

I have this code for the login, and it doesn't authenticate the user, it gives me back the error response I wrote. I tried changing it so the user would stop giving back "null", but nothing is functioning. This is the code:
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username
});
!user && res.status(401).json("Wrong Credentials");
const hashedPassword = CryptoJS.AES.decrypt(
user.password,
process.env.PASS_SEC
);
const OriginalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
OriginalPassword !== req.body.password &&
res.status(401).json("Wrong Password!");
const accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin,
}, process.env.JWT_SEC,
{expiresIn:"3d"}
);
const { password, ...others } = user._doc;
res.status(200).json(...others, accessToken);
} catch (err) {
res.status(500).json(err);
}
});
what it gives back is the "Wrong Credentials" string I put; I console logged the req.body.username and it gives me back my username, but the const user just keeps giving back null.
I hope I can find some answer, I'm losing hope already
const { username, password } = req.body;
try {
const getUser=user.findOne({username:username})
if (!getUser) {
return res.json({err:"User Doesn't Exists"})
} else {
const hashedPassword = CryptoJS.AES.decrypt(
getUser.password,
process.env.PASS_SEC
);
const OriginalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
if (user.password === OriginalPassword) {
const accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin,
}, process.env.JWT_SEC, {
expiresIn: "3d"
});
const { password, ...others } = user._doc;
res.status(200).json(...others, accessToken,{ message: "Logged in
Successfully"});
} else {
return res.json({ err: "Password Doesn't Match" })
}
}
} catch(err) {
return res.json(err)
}

How to determine return value is an error when using MVC?

I was trying to separate the logic of my back-end server into route/controller/service,
but one problem is when I try to catch error in service layer, I will return error, but how can I determine if it's error and return 404 status in my controller layer ?
Here's the code
Service layer
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return savedUser
} catch (err){
return err
}
};
Controller layer
const register = (req,res) => {
const { name, email, password } = req.body
const user = UserService.register(name,email,password);
if(how to determine here?){
res.status(201).json(user);
}
}
You could return an object in your Service Layer instead of an User or Error object:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return { user: savedUser, error: null }
} catch (error){
return { error, user: null }
}
};
In your Controller Layer you would then do something like this:
const register = (req,res) => {
const { name, email, password } = req.body
const { user, error } = UserService.register(name,email,password);
if(error){
res.status(500).json({ user, error: error.message });
} else {
res.status(200).json({ user, error })
}
}
return the save promise and handle errors in your controller
serivce:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
return newUser.save();
};
controller:
const register = async (req,res) => {
const { name, email, password } = req.body
try {
const user = await UserService.register(name,email,password);
res.status(201).json(user);
} catch(error) {
// handle error here
}
}

User verification changing 'registered user hashed password', preventing login

User registers with a bcrypt hashed password. After proceding with email verification through mailtrap/nodemailer and successfully verifing user status to true, the login fails, returning 'unmatched password", because it is changing stored user hashed password. If I skip the verification step and proceed with login directly, it will work fine. So how do I do 'user verification' without changing stored hashed password in my code below?
async login(req: Request, res: Response){
// Get user from database
const userRepository = getRepository(User);
const { email, password } = req.body;
if (!(email && password)) {
console.error('Empty name or password!');
return res.status(400).send({
error: 'Empty name or password'
});
}
const user = await userRepository.findOne({ where: { email } });
let storedUserPass = user!.password;
console.log(password) //-> consoles plain typed text
console.log(storedUserPass) //-> consoles encrypted password
const isValidPassword = await bcrypt.compareSync(password, storedUserPass as string);
console.log(isValidPassword) //-> consoles false
if(!user) {
return res.send(`User doesn t exist! `);
}
else if (!user.isVerified) {
return res.send('Your Email has not been verified. Please do so!');
}
else if(!isValidPassword) {
return res.send('Password doesn t match')
}
else {
// Sing JWT, valid for 1 hour
const token = jwt.sign(
{ id: user.id, email: user.email },
process.env.NODE_ENV,
{ expiresIn: "1d" }
);
res.json({
user,
token
});
}
},
async create(req: Request, res: Response) {
const verificationToken = await crypto.randomBytes(8).toString('hex');
const { name, email, password, role, isVerified } = req.body;
const date = new Date();
try {
const userRepository = getRepository(User);
const tokenRepository = getRepository(Token);
//User validation
const data = {
name,
email,
password,
}
const schema = Yup.object().shape({
name: Yup.string().required('Name required.').max(60),
email: Yup.string().email("Invalid Email.").required(),
password: Yup.string().required().min(4).max(10),
})
await schema.validate(data, {
abortEarly: false
});
const userExists = await userRepository.findOne({ where: { email }})
if(userExists) {
return res.send(`Usuário com email cadastrado ${email} já existe!` );
} else {
const token = tokenRepository.create({token: verificationToken, tokenDate: date})
await tokenRepository.save(token);
const user = userRepository.create({ name, email, password, role: role || "basic", isVerified, date, token})
console.log(user.password) // consoles plain text
await userRepository.save(user);
console.log(user.password) //consoles encrypted text
return new Promise((resolve,reject)=>{
const transp = transport;
var mailOptions = {
from: 'Administrador <c3e26a9df0-703049#inbox.mailtrap.io>',
to: email,
subject: 'Account verification link!',
html: `<h2>Olá ${user.name}</h2><br/>
<p>Finalize o seu cadastro clicando neste <a href="http://${req.headers.host}/users/confirmation/${user.email}/${verificationToken}" target="_about" style="color: blue, text-derocation: none"}>Link</a>`
}
transp.sendMail(mailOptions, function(error: Err){
if (error) {
return res.status(500).send({msg:'Technical Issue!,Please click on resend for verify your Email.'});
}
else {
return res.send('A verification email has been sent to ' + user.email + '. It will be expire after one day. If you not get verification Email click on resend token.');
});
})
}
} catch(err) {
return res.status(404).send({ err: "Failed to send email."})
}
},
//TYPEORM USER MODEL:
#Entity('users')
export default class User {
#PrimaryGeneratedColumn('uuid')
id: number;
#Column()
name: string;
#Column({
unique: true
})
email: string;
#Column()
password: string;
#Column()
role: string;
default: 'basic'
enum: ["basic", "supervisor", "admin"];
#Column({
type: "datetime"
})
date!: Date;
#Column({
default: false
})
isVerified: boolean;
#BeforeInsert()
#BeforeUpdate()
hashPassword() {
this.password = bcrypt.hashSync(this.password, 8); // salt de 8
}
checkIfUnencryptedPasswordIsValid(unencryptedPassword: string) {
return bcrypt.compareSync(unencryptedPassword, this.password);
}
#OneToMany(() => Orphanage, orphanage => orphanage.user, {
cascade: ['insert' , 'update']
})
#JoinColumn({ name: 'user_id'})
orphanages: Orphanage[];
#OneToOne(type => Token)
#JoinColumn()
token: Token;
}
async confirmEmail(req: Request, res: Response) {
try {
const userRepository = getRepository(User);
const tokenRepository = getRepository(Token);
const tokenExists = await tokenRepository.findOne({ token: req.params.token });
tokenExists!.tokenDate
if( !tokenExists ) {
return res.status(400).send({msg:'Your verification link may have expired. Please click on resend for verify your Email.'});
} else {
const user = await userRepository.findOne({where: { token: tokenExists, email: req.params.email }})
//user not exist
if(!user) {
return res.status(401).send({msg:'We were unable to find a user for this verification. Please SignUp!'});
} // user is already verified
else if (user.isVerified){
return res.status(200).send('User has been already verified. Please Login');
} else { //verify user
user.isVerified = true;
await userRepository.save(user);
console.log(user) // -> hashed user.password is different from when created user after user is verified.
return res.status(200).send('Your account has been successfully verified')
}
}
} catch(err) {
return res.status(500).send({err: "Sorry, it could not be validated!"});
}
},
Using ? in user?.password is not a valid way. The symbol? is used in the ternary operator. The ternary operator needs three parameters and you are not providing these parameters.
solution: format your code as given below. I used the ! operator in user!.password. This operator in user! ensures your transpiler that the user will not be undefined. So, before using this operator you should check whether a user is defined or not, in this way:
if(!user) {
return res.send(`User doesn t exist! `);
}
If you don't check the user before using ! you may get a run-time error in the case of an undefined user. No need to use as string.
Changed Block in your code:
const user = await userRepository.findOne({ where: { email } });
//check user immediately after getting it to avoid errors
if(!user) {
return res.send(`User doesn t exist! `);
}
let storedUserPass = user!.password;
const isValidPassword = await bcrypt.compareSync(password, storedUserPass);
Now your code will look like:
async login(req: Request, res: Response){
// Get user from database
const userRepository = getRepository(User);
const { email, password } = req.body;
if (!(email && password)) {
console.error('Empty name or password!');
return res.status(400).send({
error: 'Empty name or password'
});
}
const user = await userRepository.findOne({ where: { email } });
if(!user) {
return res.send(`User doesn t exist! `);
}
let storedUserPass = user!.password;
const isValidPassword = await bcrypt.compareSync(password, storedUserPass);
if (!user.isVerified) {
return res.send('Your Email has not been verified. Please do so!');
}
else if(!isValidPassword) {
return res.send('Password doesn t match')
}
else {
// Sing JWT, valid for 1 hour
const token = jwt.sign(
{ id: user.id, email: user.email },
process.env.NODE_ENV,
{ expiresIn: "1d" }
);
res.json({
user,
token
});
}
}
Shakir Aqeel, please do not consider my last answer, I got confused, the prob has nothing to do with token.
But I have always used same library to encrypt the password when creating user, located on User model:
hashPassword() {
this.password = bcrypt.hashSync(this.password, 8);
}
In the User controller:
console.log(user.password) -> plain typed password
await userRepository.save(user);
console.log(user.password) -> encrypted password
If I register a new user with verification status as 'true', it will login with no problem....

How to display errors with Axios in React

Good day,
i am running to a slight problem with Axios, i want to check if the status code is ok, if not then i send the error to the client. How can i do that ?
This is my attempt to do it, but i had no success:
Route code:
router.post("/register", async (req, res) => {
try {
//check if fields are empty
const { name, email, password, rePassword } = req.body;
if (!name || !email || !password || !rePassword) {
return res.status(406).send("fields are empty");
}
//passsword length should be at leats 6 characters
if (password.length < 6) {
return res
.status(406)
.send("Password should at least be 6 characters long");
}
//checking if passwords match
if (password !== rePassword) {
return res.status(406).send("Passwords dont match");
}
//check if user exists
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) {
return res.status(406).send("User already exists");
} else {
//create salt
const salt = await genSalt(10);
//hashing the password
const hashedPassword = await hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
isAdmin: req.body.isAdmin,
});
const newUser = await user.save();
res.status(200).send(newUser);
}
} catch (err) {
console.log(err);
}
});
Page code:
const handleSubmit = async e => {
e.preventDefault();
axios
.post("http://localhost:5000/users/register", {
name,
email,
password,
rePassword,
})
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}
The response will give you the following options
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
This will allow you to check for valid stausText/codes, or display errors accordingly.

Cannot Save and Activate the Registered Email Account

I have successfully send a token in my Email upon Registration via Send Grid
router.post(
'/',
[
check('lastname', 'Lastname is required').not().isEmpty(),
check('firstname', 'Firstname is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { lastname, firstname, email, password } = req.body;
try {
// Identify if users exists
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
errors: [{ msg: 'User already exists' }],
});
}
// Get users avatar
const avatar = gravatar.url(email, {
// size
s: '200',
// rating
r: 'pg',
// default (mm = default image)
d: 'mm',
});
// create a new instance of a user
user = new User({
lastname,
firstname,
email,
avatar,
password,
});
// // Encrypt password
// // salt to do the hashing
const salt = await bcrypt.genSalt(10);
// // creates a hash and put to the user.password
user.password = await bcrypt.hash(password, salt);
const token = jwt.sign(
{
user,
},
accountActivation,
{
expiresIn: 360000,
}
);
const emailData = {
from: emailFrom,
to: user.email,
subject: 'Account Activation',
html: `
<h1>Please use the following to activate your account</h1>
<p>${PORT}/activeprofile/${token}</p>
<hr />
<p>This email may contain sensetive information</p>
<p>${PORT}</p>
`,
};
sgMail
.send(emailData)
.then(() => {
return res.json({
message: `Email has been sent to ${email}`,
});
})
.catch((err) => {
return res.status(400).json({
msg: 'Unable to send',
});
});
// await user.save()
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
and I have successfully get an Email with it's token.
whenever I am trying to verify it in my postman.
router.post('/activeprofile', (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, accountActivation, (err) => {
if (err) {
console.log('Activation error');
return res.status(401).json({
errors: 'Expired link. Signup again',
});
} else {
const { lastname, firstname, email, password } = jwt.decode(
token
);
// create a new instance of a user
const user = new User({
lastname: req.body.lastname,
firstname,
email,
password,
});
user.save((err, user) => {
if (err) {
console.log('Error Saving the User', err.message);
return res.status(401).json({ msg: 'Unable to save' });
} else {
return res.status(200).json({
success: true,
message: user,
message: 'Signup Success',
});
}
});
}
});
} else {
return res.json({
message: 'Error happened, Please try again later',
});
}});
I always get this Error.
I even tried doing
const user = new User({
lastname: req.body.lastname,
firstname: req.body.firstname,
email: req.body.email,
password: req.body.passsword,
});
still I ended up with all the same errors in the picture posted.
Btw. this is my User Schema
const UserSchema = new mongoose.Schema({
lastname: {
type: String,
required: true,
},
firstname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
date: {
type: Date,
default: Date.now,
},}); module.exports = Use = mongoose.model('user', UserSchema);
my postman error:
I manage to figure out the problem. It's because I have created a double instance of the user
so I removed the user instance from the registration
router.post(
'/',
[
check('lastname', 'Lastname is required').not().isEmpty(),
check('firstname', 'Firstname is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 }),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { lastname, firstname, email, password } = req.body;
try {
// Identify if users exists
let data = await User.findOne({ email });
if (data) {
return res.status(400).json({
errors: [{ msg: 'User already exists' }],
});
}
const token = jwt.sign(
{
lastname,
firstname,
email,
password,
},
accountActivation,
{
expiresIn: 360000,
}
);
const emailData = {
from: emailFrom,
to: email,
subject: 'Account Activation',
html: `
<h1>Please use the following to activate your account</h1>
<p>${PORT}/activeprofile/${token}</p>
<hr />
<p>This email may contain sensetive information</p>
<p>${PORT}</p>
`,
};
sgMail
.send(emailData)
.then(() => {
return res.json({
message: `Email has been sent to ${email}`,
});
})
.catch((err) => {
return res.status(400).json({
msg: 'Unable to send',
});
});
// await user.save()
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
and put it in the account activation together with the gravatar and the hashing of the passwords
router.post('/activeprofile', (req, res) => {
const { token } = req.body;
if (token) {
jwt.verify(token, accountActivation, async (err, decoded) => {
if (err) {
console.log('Activation error');
return res.status(401).json({
errors: 'Expired link. Signup again',
});
}
const { lastname, firstname, email, password } = jwt.decode(token);
// // Get users avatar
const avatar = gravatar.url(email, {
// size
s: '200',
// rating
r: 'pg',
// default (mm = default image)
d: 'mm',
});
// create a new instance of a user
let user = new User({
lastname,
firstname,
email,
avatar,
password,
});
// Encrypt password
// salt to do the hashing
const salt = await bcrypt.genSalt(10);
// creates a hash and put to the user.password
user.password = await bcrypt.hash(password, salt);
user.save((err, user) => {
if (err) {
console.log('Error Saving the User', err.message);
return res.status(401).json({ msg: 'Unable to save' });
} else {
return res.status(200).json({
success: true,
message: user,
message: 'Signup Success',
});
}
});
});
} else {
return res.json({
message: 'Error happened, Please try again later',
});
}});
and it's successful, the user is saved in the database.

Resources