Files
Zumri-Backend/app/utils/passwordReset.utill.js
T
Sathira Sri Sathara 9d3d431416 feat: implement identity and security features
- Added account types and privileged account types constants.
- Created admin user controller for updating user security fields.
- Developed role assignment controller for managing user roles.
- Implemented validation middleware for request schemas.
- Defined user role and auth session models for database interactions.
- Created services for authentication, email notifications, and OTP handling.
- Developed OAuth service for Google and Apple authentication.
- Added JWT utility functions for token generation and verification.
- Implemented comprehensive tests for authentication, session management, and password policies.
- Created migration for updating user schema and adding new tables for auth sessions and user identities.
2026-09-03 13:56:18 +05:30

143 lines
3.0 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 { sendMail } = require("./mail.util");
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 sendMail({
to: email,
subject:
"Reset your ZUMRI password",
templateName:
"passwordReset",
templateVars: {
firstName: firstName,
resetLink: resetLink,
expiryTime: "15 minutes",
},
text:
`Hi ${firstName}, use this link within 15 minutes to reset your password: ${resetLink}`,
});
};
const sendPasswordChangedEmail = async (
email,
firstName
) => {
const changedAt =
new Date().toLocaleString();
await sendMail({
to: email,
subject:
"Your ZUMRI password was changed",
templateName:
"passwordChanged",
templateVars: {
customer_name:
firstName,
changed_at:
changedAt,
},
text:
`Hi ${firstName}, your password was changed at ${changedAt}. If this was not you, contact support immediately.`,
});
};
module.exports = {
createPasswordReset,
verifyPasswordResetToken,
deletePasswordReset,
consumePasswordResetToken,
hashPasswordResetToken,
sendPasswordResetEmail,
sendPasswordChangedEmail,
};