From 841ecf2008b8934bb5532c724219b0b3d0eb334b Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Fri, 6 Mar 2026 12:44:32 +0100 Subject: [PATCH 1/2] feat:Add Change Password Endpoint #28 --- src/controllers/auth.controller.js | 63 ++++++++++++++++++++++++++++++ src/routes/auth.routes.js | 8 ++++ src/validators/auth.validators.js | 13 ++++++ 3 files changed, 84 insertions(+) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 329f03d..55d3d3d 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -439,6 +439,68 @@ const refreshToken = async (req, res, next) => { return next(error); } }; +/** + * Change authenticated user's password + * PATCH /api/auth/change-password + * + * Add this function to src/controllers/auth.controller.js + * and include it in the module.exports object at the bottom. + */ +const changePassword = async (req, res, next) => { + try { + const { currentPassword, newPassword } = req.body; + + // req.userId is set by the authenticate middleware (same as logout) + const user = await User.findById(req.userId).select('+password'); + if (!user) { + const error = new Error('User not found'); + error.statusCode = 404; + error.isOperational = true; + return next(error); + } + + // Verify the supplied current password against the stored hash + const isCurrentPasswordValid = await bcrypt.compare( + currentPassword, + user.password + ); + if (!isCurrentPasswordValid) { + const error = new Error('Current password is incorrect'); + error.statusCode = 401; + error.isOperational = true; + return next(error); + } + + // Prevent reuse of the same password + const isSamePassword = await bcrypt.compare(newPassword, user.password); + if (isSamePassword) { + const error = new Error( + 'New password must be different from the current password' + ); + error.statusCode = 400; + error.isOperational = true; + return next(error); + } + + // Assign plain-text password — the User model's pre-save hook hashes it + user.password = newPassword; + + // Invalidate all existing refresh tokens (force re-login on other devices) + user.refreshTokenHash = null; + user.refreshTokenExpiresAt = null; + + await user.save(); + + return sendSuccess( + res, + {}, + 200, + 'Password changed successfully. Please log in again.' + ); + } catch (error) { + return next(error); + } +}; module.exports = { register, @@ -448,4 +510,5 @@ module.exports = { forgotPassword, verifyEmail, refreshToken, + changePassword, }; diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js index 5e5d799..c9b871f 100644 --- a/src/routes/auth.routes.js +++ b/src/routes/auth.routes.js @@ -45,4 +45,12 @@ router.get('/verify-email/:token', verifyEmail); // POST /api/auth/refresh-token - Refresh access token using refresh token router.post('/refresh-token', validate(refreshTokenSchema), refreshToken); +// PATCH /api/auth/change-password - Change password for authenticated user +router.patch( + '/change-password', + authenticate, + validate(changePasswordSchema), + changePassword +); + module.exports = router; diff --git a/src/validators/auth.validators.js b/src/validators/auth.validators.js index 65ca5a6..ea8150c 100644 --- a/src/validators/auth.validators.js +++ b/src/validators/auth.validators.js @@ -52,10 +52,23 @@ const refreshTokenSchema = Joi.object({ }), }); +const changePasswordSchema = Joi.object({ + currentPassword: Joi.string().required().messages({ + 'string.empty': 'Current password is required', + 'any.required': 'Current password is required', + }), + newPassword: Joi.string().min(8).required().messages({ + 'string.empty': 'New password is required', + 'string.min': 'New password must be at least 8 characters', + 'any.required': 'New password is required', + }), +}); + module.exports = { registerSchema, loginSchema, resetPasswordSchema, forgotPasswordSchema, refreshTokenSchema, + changePasswordSchema, }; From ac5e585638963d1ef877e335ed6c18c15cc2f5cb Mon Sep 17 00:00:00 2001 From: OlufunbiIK Date: Fri, 6 Mar 2026 12:47:20 +0100 Subject: [PATCH 2/2] fix:fixed lint error --- src/routes/auth.routes.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js index c9b871f..d55f02c 100644 --- a/src/routes/auth.routes.js +++ b/src/routes/auth.routes.js @@ -7,6 +7,7 @@ const { forgotPassword, verifyEmail, refreshToken, + changePassword, } = require('../controllers/auth.controller'); const validate = require('../middlewares/validate'); const authenticate = require('../middlewares/auth'); @@ -16,6 +17,7 @@ const { resetPasswordSchema, forgotPasswordSchema, refreshTokenSchema, + changePasswordSchema, } = require('../validators/auth.validators'); const router = express.Router();