I am building api authentication routes using nodejs and express. The login and registration routes work perfectly until I try verifying a user by sending them a verification link via email. The user password is being changed, therefore after the email being verified, the user is unable to login. I don’t get what is happening to my code. I am encrypting my passwords using bcrypt but when try using unencrypted passwords, everything works. The password is only changed after email verification when I use bcrypt encryption! I need help please!
Here is my code:
User Model:
import { Schema, model } from "mongoose";
import bcrypt from 'bcrypt';
const UserSchema = new Schema({
Admin: {
type: Boolean,
default: false
},
name: {
type: String,
required: [true, 'name field required!']
},
email: {
type: String,
required: [true, 'email field required'],
unique: [true, 'email already taken!']
},
password: {
type: String,
required: [true, 'password field required']
},
verified: {
type: Boolean,
default: false
},
verificationToken: {
type: String
},
addresses: [
{
name: String,
mobileNo: String,
houseNo: String,
streetNo: String,
landMark: String,
city: String,
country: String,
postalCode: String
}
],
orders: [
{
type: Schema.Types.ObjectId,
ref: 'Order'
}
]
}, { timestamps: true });
/** fire function before saving document */
UserSchema.pre('save', async function (next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next();
});
/** fire function after saving document */
UserSchema.post("save", async (doc, next) => {
console.log("User has been created!", doc);
next();
});
/** fire this function to login the user */
UserSchema.statics.login = async function (email, password) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error('Wrong password!')
}
throw Error('Email does not exist!')
}
const User = model('User', UserSchema);
export default User;
Controllers:
import crypto from 'crypto';
import User from "../models/User.js";
import sendEmailVerification from '../lib/nodemailer.js';
import { createToken, maxAge } from "../lib/token.js";
/**
* ! Register New User
*/
const registerUser = async (req, res) => {
try {
const { name, email, password } = req.body;
/** check if user exists */
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(401).json({message: 'User already exists!'})
}
const user = await User.create({
name,
email,
password,
verificationToken: crypto.randomBytes(20).toString('hex')
});
/** send verification email */
await sendEmailVerification(user.email, user.verificationToken);
return res.status(200).json({message: 'User registration successful'})
} catch (error) {
console.log(error);
return res.status(500).json({error: error.message})
}
}
/**
* ? Verify verification token
*/
const verifyToken = async (req, res) => {
try {
const { token } = req.params;
/** find user with the given verification token */
const user = await User.findOne({ verificationToken: token });
if (!user) {
return res.status(401).json({message: 'Invalid verification token!'})
}
/** mark as verified */
user.verified = true;
user.verificationToken = undefined;
await user.save();
res.status(200).json({message: 'User token has been verified!'})
} catch (error) {
console.log(error);
return res.status(500).json({message: 'Token verification failed!'})
}
}
/**
* ! Login User;
*/
const loginUser = async (req, res) => {
try {
const { email, password } = req.body;
/** check if user is verified */
const user = await User.login(email, password);
const token = await createToken(user._id, user.email, user.Admin, user.name);
res.cookie("authToken", token, { maxAge: maxAge * 1000, httpOnly: true})
res.status(200).json({ user });
} catch (error) {
console.log(error);
return res.status(500).json({error: error.message})
}
}
export {
registerUser,
verifyToken,
loginUser
}
Routes:
import { Router } from "express";
import { loginUser, registerUser, verifyToken } from "../controllers/auth.js";
const router = Router();
/**
* ! Create New User
*/
router.post("/register", registerUser);
/**
* ? Verify verification token
*/
router.get('/verify/:token', verifyToken);
/**
* ! Login User;
*/
router.post("/login", loginUser);
export default router;