feat: cambiar clave desde la app (usuario logueado)

Nuevo PUT /api/auth/change-password: exige la clave actual (a diferencia de
reset-password, que usa un token de correo sin sesion). Opcion "Cambiar clave"
agregada al menu de usuario (popup), disponible para cualquier rol.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-06 12:35:25 -05:00
parent a6824db421
commit 8c06d20720
11 changed files with 99 additions and 5 deletions
+17
View File
@@ -170,3 +170,20 @@ export async function resetPassword(token, newPassword) {
);
await logAudit({ userId: user.id, action: 'reset_password', entityType: 'user', entityId: user.id });
}
// Cambio de clave desde la app, con sesion activa (distinto del olvide-clave: exige la clave actual).
export async function changePassword(userId, currentPassword, newPassword) {
if (!currentPassword || !newPassword || newPassword.length < 8) {
throw new HttpError(400, 'La clave actual y una clave nueva de al menos 8 caracteres son obligatorias');
}
const [rows] = await pool.query('SELECT password_hash FROM users WHERE id = ?', [userId]);
const user = rows[0];
if (!user) throw new HttpError(404, 'Usuario no encontrado');
const validPassword = await bcrypt.compare(currentPassword, user.password_hash);
if (!validPassword) throw new HttpError(400, 'La clave actual no es correcta');
const passwordHash = await bcrypt.hash(newPassword, 12);
await pool.query('UPDATE users SET password_hash = ? WHERE id = ?', [passwordHash, userId]);
await logAudit({ userId, action: 'change_password', entityType: 'user', entityId: userId });
}