// app/utils/emailVerification.util.js const crypto = require("crypto"); const emailService = require("../services/email/email.service"); const redis = require("../config/redisClient"); const EMAIL_VERIFICATION_TTL = Number(process.env.EMAIL_VERIFICATION_TTL_SECONDS) || 1800; const generateEmailVerificationToken = () => { return crypto.randomBytes(32).toString("hex"); }; const hashEmailVerificationToken = (token) => { return crypto.createHash("sha256").update(token).digest("hex"); }; const createEmailVerification = async (userId) => { const previousKey = await redis.get(`email-verification-user:${userId}`); if (previousKey) await redis.del(previousKey); // 1. Generate raw token const token = generateEmailVerificationToken(); // 2. Hash token const tokenHash = hashEmailVerificationToken(token); // 3. Create Redis key const redisKey = `email-verification:${tokenHash}`; await redis.set(redisKey, userId, "EX", EMAIL_VERIFICATION_TTL); await redis.set(`email-verification-user:${userId}`, redisKey, "EX", EMAIL_VERIFICATION_TTL); return token; }; /** * Check a verification token. */ const verifyEmailVerificationToken = async (token) => { if (!token || typeof token !== "string") { return null; } // 1. Hash token received from user const tokenHash = hashEmailVerificationToken(token); // 2. Build same Redis key const redisKey = `email-verification:${tokenHash}`; // 3. Search Redis const userId = await redis.getdel(redisKey); if (!userId) { return null; } await redis.del(`email-verification-user:${userId}`); return { userId, redisKey, }; }; //send verification email to user const sendVerificationEmail = async (email, firstName, verificationToken) => { const confirmationLink = `${process.env.FRONTEND_URL}/verify-email?token=${verificationToken}`; // Send email await emailService.send({ recipient: email, templateKey: "emailVerification", variables: { customer_name: firstName, confirmation_link: confirmationLink, }, eventId: `verify-${hashEmailVerificationToken(verificationToken)}`, }); }; const deleteEmailVerification = async (redisKey) => { await redis.del(redisKey); }; const consumeEmailVerificationToken = async (token) => { if (!token || typeof token !== "string") return null; const redisKey = `email-verification:${hashEmailVerificationToken(token)}`; const userId = await redis.getdel(redisKey); if (userId) await redis.del(`email-verification-user:${userId}`); return userId; }; module.exports = { createEmailVerification, verifyEmailVerificationToken, sendVerificationEmail, deleteEmailVerification, consumeEmailVerificationToken, hashEmailVerificationToken, };