Add change password functionality with validation and email notification
This commit is contained in:
@@ -374,7 +374,119 @@ exports.resetPassword = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
exports.changePassword = async (req, res) => {
|
||||
try {
|
||||
// User ID comes from authenticate middleware
|
||||
const userId = req.user.id;
|
||||
|
||||
const { currentPassword, newPassword, confirmPassword } = req.body;
|
||||
|
||||
// 1. Check required fields
|
||||
if (!currentPassword || !newPassword || !confirmPassword) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
message:
|
||||
"Current password, new password and confirm password are required",
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Check new password and confirmation
|
||||
if (newPassword !== confirmPassword) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
message: "New password and confirm password do not match",
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Validate password policy
|
||||
const passwordValid = validatePassword(newPassword);
|
||||
|
||||
if (!passwordValid) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
message: "New password does not meet the required criteria",
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Get logged-in user from database
|
||||
const user = await User.findByPk(userId);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Check current password
|
||||
const currentPasswordValid = await checkPassword(
|
||||
currentPassword,
|
||||
user.password,
|
||||
);
|
||||
|
||||
if (!currentPasswordValid) {
|
||||
return res.status(401).send({
|
||||
success: false,
|
||||
message: "Current password is incorrect",
|
||||
});
|
||||
}
|
||||
|
||||
// 6. Make sure new password is different
|
||||
const sameAsOldPassword = await checkPassword(newPassword, user.password);
|
||||
|
||||
if (sameAsOldPassword) {
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
message: "New password must be different from current password",
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Hash new password
|
||||
const hashedPassword = await hashPassword(newPassword);
|
||||
|
||||
// 8. Update user
|
||||
user.password = hashedPassword;
|
||||
user.passwordChangedAt = new Date();
|
||||
|
||||
await user.save();
|
||||
|
||||
// 9. Revoke all refresh sessions
|
||||
deleteAllUserSessions(user.id);
|
||||
|
||||
// 10. Clear auth cookies
|
||||
res.clearCookie("access_token", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
});
|
||||
|
||||
res.clearCookie("refresh_token", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
});
|
||||
|
||||
// 11. Send confirmation email
|
||||
try {
|
||||
await sendPasswordChangedEmail(user.email, user.firstName);
|
||||
} catch (mailError) {
|
||||
console.error("PASSWORD CHANGED EMAIL ERROR:", mailError);
|
||||
}
|
||||
|
||||
// 12. Response
|
||||
return res.status(200).send({
|
||||
success: true,
|
||||
message: "Password changed successfully. Please login again.",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("CHANGE PASSWORD ERROR:", error);
|
||||
|
||||
return res.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to change password",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Logout: Clear the JWT cookie
|
||||
exports.logout = async (req, res) => {
|
||||
@@ -390,9 +502,7 @@ exports.logout = async (req, res) => {
|
||||
if (session) {
|
||||
deleteRefreshSession(session.sessionId);
|
||||
|
||||
console.log(
|
||||
`Refresh session deleted: ${session.sessionId}`
|
||||
);
|
||||
console.log(`Refresh session deleted: ${session.sessionId}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,20 +510,14 @@ exports.logout = async (req, res) => {
|
||||
res.clearCookie("access_token", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "None"
|
||||
: "Lax",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
});
|
||||
|
||||
// 5. Clear refresh token cookie
|
||||
res.clearCookie("refresh_token", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "None"
|
||||
: "Lax",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
});
|
||||
|
||||
// 6. Send response
|
||||
@@ -421,7 +525,6 @@ exports.logout = async (req, res) => {
|
||||
success: true,
|
||||
message: "Logged out successfully",
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("LOGOUT ERROR:", error);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user