/** * Copyright (c) 2026 Niolla * All rights reserved. * * This source code is proprietary and confidential. * Unauthorized copying, modification, distribution, or use * of this file, via any medium, is strictly prohibited. */ // app/utils/passwordReset.util.js const crypto = require("crypto"); const redis = require("../config/redisClient"); const emailService = require("../services/email/email.service"); const PASSWORD_RESET_TTL = Number(process.env.PASSWORD_RESET_TTL_SECONDS) || 900; const generatePasswordResetToken = () => { return crypto.randomBytes(32).toString("hex"); }; const hashPasswordResetToken = (token) => { return crypto.createHash("sha256").update(token).digest("hex"); }; const createPasswordReset = async (userId) => { // 1. Generate RAW token const token = generatePasswordResetToken(); // 2. Hash RAW token const tokenHash = hashPasswordResetToken(token); // 3. Create Redis key const redisKey = `password-reset:${tokenHash}`; // 4. Save user ID with 15 minute TTL await redis.set(redisKey, userId, "EX", PASSWORD_RESET_TTL); // Send only RAW token to user return token; }; const verifyPasswordResetToken = async (token) => { if (!token || typeof token !== "string") { return null; } // Hash token received from user const tokenHash = hashPasswordResetToken(token); // Create same Redis key const redisKey = `password-reset:${tokenHash}`; // Search Redis const userId = await redis.get(redisKey); if (!userId) { return null; } return { userId, redisKey, }; }; const deletePasswordReset = async (redisKey) => { await redis.del(redisKey); }; const consumePasswordResetToken = async (token) => { if (!token || typeof token !== "string") return null; return redis.getdel(`password-reset:${hashPasswordResetToken(token)}`); }; const sendPasswordResetEmail = async (email, firstName, resetToken) => { const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`; await emailService.send({ recipient: email, templateKey: "passwordReset", variables: { firstName: firstName, resetLink: resetLink, expiryTime: "15 minutes", }, eventId: `reset-${hashPasswordResetToken(resetToken)}`, }); }; const sendPasswordChangedEmail = async ( email, firstName ) => { const changedAt = new Date().toLocaleString(); await emailService.send({ recipient: email, templateKey: "passwordChanged", variables: { customer_name: firstName, changed_at: changedAt, }, }); }; module.exports = { createPasswordReset, verifyPasswordResetToken, deletePasswordReset, consumePasswordResetToken, hashPasswordResetToken, sendPasswordResetEmail, sendPasswordChangedEmail, };