b6b345f245
- Refactor email verification and password reset utilities to use new email service. - Introduce email delivery queue and notification delivery model for better tracking. - Enhance file validation and storage services for improved security and ownership management. - Add cron job for cleaning inactive notifications with retention policy. - Update document worker to handle document generation and storage more efficiently. - Implement logging improvements in activity and log workers. - Create comprehensive documentation for new API endpoints and services. - Add unit tests for file validation and notification policies to ensure robustness.
123 lines
2.8 KiB
JavaScript
123 lines
2.8 KiB
JavaScript
/**
|
|
* 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,
|
|
};
|