From 3ffbcbfcc9b860ea293905678953ff98f48853a8 Mon Sep 17 00:00:00 2001 From: Bhaskar Date: Wed, 6 Aug 2025 13:20:05 +0530 Subject: [PATCH] forgot password changes --- src/controllers/userController.js | 315 +++++++++++++++++------ src/index.js | 414 +++++++++++++++--------------- 2 files changed, 450 insertions(+), 279 deletions(-) diff --git a/src/controllers/userController.js b/src/controllers/userController.js index 8dc715db..9bdc703f 100644 --- a/src/controllers/userController.js +++ b/src/controllers/userController.js @@ -282,6 +282,52 @@ exports.addUser = async (req, reply) => { // Accepts a user , password , and checks in the system to see if user exists , and password is valid // returns a user object so that jwt token can be created and sent back to the client +// exports.loginUser = async (req, fcmIds, deviceId) => { +// try { +// const { phone, password } = req.body; +// let user = await User.findOne({ phone }); +// let isStaff = false; +// let staffMember = null; + +// // If not a main user, check staff inside all users +// if (!user) { + +// const users = await User.find({ "staff.staff.phone": phone }); +// for (const u of users) { +// const foundStaff = u.staff.staff.find((s) => s.phone === phone); +// if (foundStaff) { +// user = u; // Assign user as the main user under which the staff exists +// staffMember = foundStaff; +// isStaff = true; +// break; +// } +// } +// } + +// // If no user or staff found, return invalid credentials +// if (!user) return { same: false }; + +// // Validate password +// let isSame = false; +// if (isStaff) { +// isSame = password === staffMember.password; // Plain text comparison for staff +// } else { +// isSame = await bcrypt.compare(password, user.services.password.bcrypt); // Bcrypt for main users +// } + +// if (!isSame) return { same: false }; + +// // Update deviceId +// user.deviceId = deviceId; +// await user.save(); + +// return { same: true, user, isStaff, staffMember }; +// } catch (err) { +// throw boom.boomify(err); +// } +// }; + + exports.loginUser = async (req, fcmIds, deviceId) => { try { const { phone, password } = req.body; @@ -289,14 +335,12 @@ exports.loginUser = async (req, fcmIds, deviceId) => { let isStaff = false; let staffMember = null; - // If not a main user, check staff inside all users if (!user) { - const users = await User.find({ "staff.staff.phone": phone }); for (const u of users) { const foundStaff = u.staff.staff.find((s) => s.phone === phone); if (foundStaff) { - user = u; // Assign user as the main user under which the staff exists + user = u; staffMember = foundStaff; isStaff = true; break; @@ -304,20 +348,29 @@ exports.loginUser = async (req, fcmIds, deviceId) => { } } - // If no user or staff found, return invalid credentials if (!user) return { same: false }; - // Validate password let isSame = false; + if (isStaff) { - isSame = password === staffMember.password; // Plain text comparison for staff + isSame = password === staffMember.password; } else { - isSame = await bcrypt.compare(password, user.services.password.bcrypt); // Bcrypt for main users + const otpMatch = + //user.oneTimePasswordSetFlag && + user.passwordResetCode && + password === user.passwordResetCode.toString(); + + if (otpMatch) { + isSame = true; + user.oneTimePasswordSetFlag = false; + user.passwordResetCode = null; + } else { + isSame = await bcrypt.compare(password, user.services.password.bcrypt); + } } if (!isSame) return { same: false }; - // Update deviceId user.deviceId = deviceId; await user.save(); @@ -329,6 +382,7 @@ exports.loginUser = async (req, fcmIds, deviceId) => { + exports.loginUserWithOTP = async (req) => { try { const phone = req.body.phone; @@ -571,15 +625,105 @@ exports.sendSms = async (request, reply) => { } +// exports.forgotPassword = async (req, reply) => { +// try { +// // Create a new User object from the request body +// var user = new User(req.body); + +// // Check if the request body is URL encoded +// checkFormEncoding = isUserFormUrlEncoded(req); +// if (checkFormEncoding.isUserFormUrlEncoded) { +// // Extract user information from the request body +// usertobeInserted = checkFormEncoding.user; +// user.username = usertobeInserted.username; +// user.firstName = usertobeInserted.firstName; +// user.lastName = usertobeInserted.lastName; +// user.phone = usertobeInserted.phone; +// user.emails = usertobeInserted.emails; +// } + +// // Find a user with the given phone number in the database +// userExists = await User.findOne({ +// phone: user.phone, +// }); + +// if (userExists) { +// // Generate a random password reset code +// const code = Math.floor(100000 + Math.random() * 900000); + +// // Convert the code to a string and hash it using bcrypt +// codestr = ""; +// codestr = code.toString(); +// hash = await bcryptPassword(codestr); + +// // Update the user's password reset code and password hash in the database +// const filter = { +// phone: userExists.phone, +// }; +// const update = { +// $set: { +// passwordResetCode: code, +// "services.password.bcrypt": hash, +// //oneTimePasswordSetFlag: true, +// }, +// }; +// const doc = await User.updateOne(filter, update); + +// // Find the updated user in the database +// updatedUser = await User.findOne({ phone: userExists.phone }); + +// if (updatedUser.oneTimePasswordSetFlag) { +// // Send an SMS with the password reset code +// const request = { +// body: { +// mobileNumbers: userExists.phone, +// }, +// }; +// const response = { +// send: (data) => { +// console.log(data); // Optional: Log the response from the SMS provider +// // Send a success response with the password reset code +// req.body.passwordResetCode = code; +// reply.send('{"armintatankdata":{"error":false,"forgotPassword": true}}'); +// }, +// }; +// await exports.sendSms(request, response); +// } else { +// // Send an error response if the password reset code was not set +// error = { +// armintatankdata: { +// error: true, +// code: 10007, +// message: "10007 - Unable to reset password", +// }, +// }; +// req.body.regError = error; +// reply.send(error); +// } +// } else { +// // Send an error response if no user was found with the given phone number +// error = { +// armintatankdata: { +// error: true, +// code: 10006, +// message: "10006 - Please check the phone number you entered..", +// }, +// }; +// req.body.regError = error; +// reply.send(error); +// } +// } catch (err) { +// // Handle any errors that occur during the API request +// throw boom.boomify(err); +// } +// }; + exports.forgotPassword = async (req, reply) => { try { - // Create a new User object from the request body var user = new User(req.body); - - // Check if the request body is URL encoded + checkFormEncoding = isUserFormUrlEncoded(req); if (checkFormEncoding.isUserFormUrlEncoded) { - // Extract user information from the request body usertobeInserted = checkFormEncoding.user; user.username = usertobeInserted.username; user.firstName = usertobeInserted.firstName; @@ -588,84 +732,111 @@ exports.forgotPassword = async (req, reply) => { user.emails = usertobeInserted.emails; } - // Find a user with the given phone number in the database - userExists = await User.findOne({ - phone: user.phone, - }); + const userExists = await User.findOne({ phone: user.phone }); - if (userExists) { - // Generate a random password reset code - const code = Math.floor(100000 + Math.random() * 900000); + if (!userExists) { + return reply.send({ + armintatankdata: { + error: true, + code: 10006, + message: "10006 - Please check the phone number you entered.", + }, + }); + } - // Convert the code to a string and hash it using bcrypt - codestr = ""; - codestr = code.toString(); - hash = await bcryptPassword(codestr); + // Generate a random 6-digit code + const code = Math.floor(100000 + Math.random() * 900000); - // Update the user's password reset code and password hash in the database - const filter = { - phone: userExists.phone, + // Store OTP only (not password hash) + const update = { + $set: { + passwordResetCode: code, + oneTimePasswordSetFlag: true, + }, + }; + + await User.updateOne({ phone: userExists.phone }, update); + + const updatedUser = await User.findOne({ phone: userExists.phone }); + + if (updatedUser.oneTimePasswordSetFlag) { + const request = { + body: { + mobileNumbers: userExists.phone, + }, }; - const update = { - $set: { - passwordResetCode: code, - "services.password.bcrypt": hash, - oneTimePasswordSetFlag: true, + const response = { + send: (data) => { + console.log(data); + req.body.passwordResetCode = code; + reply.send({ + armintatankdata: { + error: false, + forgotPassword: true, + }, + }); }, }; - const doc = await User.updateOne(filter, update); - - // Find the updated user in the database - updatedUser = await User.findOne({ phone: userExists.phone }); - - if (updatedUser.oneTimePasswordSetFlag) { - // Send an SMS with the password reset code - const request = { - body: { - mobileNumbers: userExists.phone, - }, - }; - const response = { - send: (data) => { - console.log(data); // Optional: Log the response from the SMS provider - // Send a success response with the password reset code - req.body.passwordResetCode = code; - reply.send('{"armintatankdata":{"error":false,"forgotPassword": true}}'); - }, - }; - await exports.sendSms(request, response); - } else { - // Send an error response if the password reset code was not set - error = { - armintatankdata: { - error: true, - code: 10007, - message: "10007 - Unable to reset password", - }, - }; - req.body.regError = error; - reply.send(error); - } + await exports.sendSms(request, response); } else { - // Send an error response if no user was found with the given phone number - error = { + return reply.send({ armintatankdata: { error: true, - code: 10006, - message: "10006 - Please check the phone number you entered..", + code: 10007, + message: "Unable to reset password", }, - }; - req.body.regError = error; - reply.send(error); + }); } } catch (err) { - // Handle any errors that occur during the API request throw boom.boomify(err); } }; +// exports.forgotPassword = async (req, reply) => { +// try { +// const user = await User.findOne({ phone: req.body.phone }); + +// if (!user) { +// return reply.send({ +// armintatankdata: { +// error: true, +// code: 10006, +// message: "10006 - Please check the phone number you entered..", +// }, +// }); +// } + +// const code = Math.floor(100000 + Math.random() * 900000).toString(); +// const hashedOTP = await bcrypt.hash(code, 10); + +// await User.updateOne( +// { phone: user.phone }, +// { +// $set: { +// "services.password.bcrypt": hashedOTP, +// temporaryPasswordCode: code, +// oneTimePasswordSetFlag: true, +// }, +// } +// ); + +// // Simulated SMS logic +// console.log("OTP sent:", code); + +// reply.send({ +// armintatankdata: { +// error: false, +// forgotPassword: true, +// }, +// }); +// } catch (err) { +// throw boom.boomify(err); +// } +// }; + + exports.changePassword = async (req, reply) => { try { diff --git a/src/index.js b/src/index.js index 7b2c9c54..f6000cc6 100644 --- a/src/index.js +++ b/src/index.js @@ -155,123 +155,6 @@ fastify.register(require('point-of-view'), { // * This is for login user as a simply user * -// fastify.post("/api/login", { -// schema: { -// description: "This is for Login User", -// tags: ["Login"], -// summary: "This is for User Login", -// body: { -// type: "object", -// required: ["phone", "password"], -// properties: { -// phone: { type: "string" }, -// password: { type: "string" }, -// fcmIds: { type: "array", items: { type: "string" }, default: [] }, -// deviceId: { type: "string" }, -// }, -// }, -// }, -// async handler(req, reply) { -// const { phone, password, fcmIds, deviceId } = req.body; -// console.log(password, phone); - -// const loginObject = await userController.loginUser(req, fcmIds, deviceId); -// console.log("loginObject",loginObject) -// if (!loginObject.same) { -// return reply.send({ -// simplydata: { -// error: true, -// code: 400, -// message: "Invalid UserId or Password supplied", -// }, -// }); -// } - -// const user = loginObject.user; -// const phoneVerified = user.phoneVerified; -// const oneTimePasswordSetFlag = user.oneTimePasswordSetFlag; - -// if (fcmIds.length > 0) { -// await User.updateOne( -// { customerId: user.customerId }, -// { $addToSet: { fcmIds: { $each: fcmIds } } } -// ); -// } - -// if (!phoneVerified) { -// return reply.send({ -// simplydata: { -// error: false, -// phoneVerified: false, -// phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, -// oneTimePasswordSetFlag, -// message: "Please Verify your phone number", -// }, -// }); -// } - -// if (oneTimePasswordSetFlag) { -// return reply.send({ -// simplydata: { -// error: false, -// phoneVerified, -// phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, -// oneTimePasswordSetFlag: true, -// message: "Password must be reset", -// }, -// }); -// } - -// const tokenPayload = { -// username: loginObject.isStaff ? loginObject.staffMember.name : user.username, -// userId: user._id, -// roles: user.profile.role, -// }; - -// const token = fastify.jwt.sign(tokenPayload, { expiresIn: "30d" }); - -// const profilePicture = await ProfilePicture.findOne({ customerId: user.customerId }); -// const responsePayload = { -// simplydata: { -// error: false, -// apiversion: fastify.config.APIVERSION, -// access_token: token, -// buildingName: user.buildingName, -// email: user.emails, -// phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, -// customerId: user.customerId, -// username: loginObject.isStaff ? loginObject.staffMember.name : user.username, -// address1: user.profile.address1, -// address2: user.profile.address2, -// phoneVerified: user.phoneVerified, -// oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, -// latitude: user.latitude, -// longitude: user.longitude, -// type: user.profile.role, -// loginType: loginObject.isStaff ? "staff" : "user", -// }, -// }; - -// if (loginObject.isStaff) { -// let allMotorAccess = loginObject.staffMember.all_motor_access; - -// // Normalize the value if it matches the given variations -// if (["view", "view only", "View", "View Only"].includes(allMotorAccess)) { -// allMotorAccess = "view"; -// } - -// responsePayload.simplydata.all_motor_access = allMotorAccess; -// } - -// if (profilePicture) { -// responsePayload.simplydata.picture = profilePicture.picture; -// } - -// reply.send(responsePayload); -// }, -// }); - - fastify.post("/api/login", { schema: { description: "This is for Login User", @@ -281,112 +164,229 @@ fastify.post("/api/login", { type: "object", required: ["phone", "password"], properties: { - phone: { type: "string", description: "Registered phone number" }, - password: { type: "string", description: "Password for authentication" }, + phone: { type: "string" }, + password: { type: "string" }, fcmIds: { type: "array", items: { type: "string" }, default: [] }, - deviceId: { type: "string" } - } - } + deviceId: { type: "string" }, + }, + }, }, async handler(req, reply) { - try { - const { phone, password, fcmIds = [], deviceId } = req.body; + const { phone, password, fcmIds, deviceId } = req.body; + console.log(password, phone); - // Find user by phone - const user = await User.findOne({ phone }); - console.log("user",user) - if (!user) { - return reply.code(400).send({ simplydata: { error: true, message: "User not found" } }); - } + const loginObject = await userController.loginUser(req, fcmIds, deviceId); + console.log("loginObject",loginObject) + if (!loginObject.same) { + return reply.send({ + simplydata: { + error: true, + code: 400, + message: "Invalid UserId or Password supplied", + }, + }); + } - // Verify password (bcrypt) - const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); - if (!isMatch) { - return reply.code(400).send({ simplydata: { error: true, message: "Invalid credentials" } }); - } + const user = loginObject.user; + const phoneVerified = user.phoneVerified; + const oneTimePasswordSetFlag = user.oneTimePasswordSetFlag; - // Update FCM Ids if present - if (fcmIds.length > 0) { - await User.updateOne( - { customerId: user.customerId }, - { $addToSet: { fcmIds: { $each: fcmIds } } } - ); - } + if (fcmIds.length > 0) { + await User.updateOne( + { customerId: user.customerId }, + { $addToSet: { fcmIds: { $each: fcmIds } } } + ); + } - // Phone Verification - if (!user.phoneVerified) { - return reply.send({ - simplydata: { - error: false, - phoneVerified: false, - phone: user.phone, - oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, - message: "Please Verify your phone number" - } - }); - } + if (!phoneVerified) { + return reply.send({ + simplydata: { + error: false, + phoneVerified: false, + phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, + oneTimePasswordSetFlag, + message: "Please Verify your phone number", + }, + }); + } - // Password reset flag - if (user.oneTimePasswordSetFlag) { - return reply.send({ - simplydata: { - error: false, - phoneVerified: user.phoneVerified, - phone: user.phone, - oneTimePasswordSetFlag: true, - message: "Password must be reset" - } - }); + // if (oneTimePasswordSetFlag) { + // return reply.send({ + // simplydata: { + // error: false, + // phoneVerified, + // phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, + // oneTimePasswordSetFlag: true, + // message: "Password must be reset", + // }, + // }); + // } + + const tokenPayload = { + username: loginObject.isStaff ? loginObject.staffMember.name : user.username, + userId: user._id, + roles: user.profile.role, + }; + + const token = fastify.jwt.sign(tokenPayload, { expiresIn: "30d" }); + + const profilePicture = await ProfilePicture.findOne({ customerId: user.customerId }); + const responsePayload = { + simplydata: { + error: false, + apiversion: fastify.config.APIVERSION, + access_token: token, + buildingName: user.buildingName, + email: user.emails, + phone: loginObject.isStaff ? loginObject.staffMember.phone : user.phone, + customerId: user.customerId, + username: loginObject.isStaff ? loginObject.staffMember.name : user.username, + address1: user.profile.address1, + address2: user.profile.address2, + phoneVerified: user.phoneVerified, + oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, + latitude: user.latitude, + longitude: user.longitude, + type: user.profile.role, + loginType: loginObject.isStaff ? "staff" : "user", + }, + }; + + if (loginObject.isStaff) { + let allMotorAccess = loginObject.staffMember.all_motor_access; + + // Normalize the value if it matches the given variations + if (["view", "view only", "View", "View Only"].includes(allMotorAccess)) { + allMotorAccess = "view"; } + + responsePayload.simplydata.all_motor_access = allMotorAccess; + } - // JWT Token Payload - const tokenPayload = { - username: user.username, - userId: user._id, - roles: user.profile.role - }; + if (profilePicture) { + responsePayload.simplydata.picture = profilePicture.picture; + } - // JWT Token Generation (matches /api/storelogin style) - const token = fastify.jwt.sign(tokenPayload, /* no direct secret here, assumes plugin config */{ expiresIn: "30d" }); + reply.send(responsePayload); + }, +}); - // Profile Picture - const profilePicture = await ProfilePicture.findOne({ customerId: user.customerId }); - // Response Construction - const responsePayload = { - simplydata: { - error: false, - message: "Login successful", - apiversion: fastify.config ? fastify.config.APIVERSION : undefined, - access_token: token, - buildingName: user.buildingName, - email: user.emails, - phone: user.phone, - customerId: user.customerId, - username: user.username, - address1: user.profile.address1, - address2: user.profile.address2, - phoneVerified: user.phoneVerified, - oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, - latitude: user.latitude, - longitude: user.longitude, - type: user.profile.role, - loginType: "user" - } - }; +// fastify.post("/api/login", { +// schema: { +// description: "This is for Login User", +// tags: ["Login"], +// summary: "This is for User Login", +// body: { +// type: "object", +// required: ["phone", "password"], +// properties: { +// phone: { type: "string", description: "Registered phone number" }, +// password: { type: "string", description: "Password for authentication" }, +// fcmIds: { type: "array", items: { type: "string" }, default: [] }, +// deviceId: { type: "string" } +// } +// } +// }, +// async handler(req, reply) { +// try { +// const { phone, password, fcmIds = [], deviceId } = req.body; - if (profilePicture) { - responsePayload.simplydata.picture = profilePicture.picture; - } +// // Find user by phone +// const user = await User.findOne({ phone }); +// console.log("user",user) +// if (!user) { +// return reply.code(400).send({ simplydata: { error: true, message: "User not found" } }); +// } - return reply.send(responsePayload); +// // Verify password (bcrypt) +// const isMatch = await bcrypt.compare(password, user.services.password.bcrypt); +// if (!isMatch) { +// return reply.code(400).send({ simplydata: { error: true, message: "Invalid credentials" } }); +// } - } catch (error) { - console.error("Login Error:", error); - return reply.code(500).send({ simplydata: { error: true, message: "Internal server error" } }); - } - } -}); +// // Update FCM Ids if present +// if (fcmIds.length > 0) { +// await User.updateOne( +// { customerId: user.customerId }, +// { $addToSet: { fcmIds: { $each: fcmIds } } } +// ); +// } + +// // Phone Verification +// if (!user.phoneVerified) { +// return reply.send({ +// simplydata: { +// error: false, +// phoneVerified: false, +// phone: user.phone, +// oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, +// message: "Please Verify your phone number" +// } +// }); +// } + +// // Password reset flag +// if (user.oneTimePasswordSetFlag) { +// return reply.send({ +// simplydata: { +// error: false, +// phoneVerified: user.phoneVerified, +// phone: user.phone, +// oneTimePasswordSetFlag: true, +// message: "Password must be reset" +// } +// }); +// } + +// // JWT Token Payload +// const tokenPayload = { +// username: user.username, +// userId: user._id, +// roles: user.profile.role +// }; + +// // JWT Token Generation (matches /api/storelogin style) +// const token = fastify.jwt.sign(tokenPayload, /* no direct secret here, assumes plugin config */{ expiresIn: "30d" }); + +// // Profile Picture +// const profilePicture = await ProfilePicture.findOne({ customerId: user.customerId }); + +// // Response Construction +// const responsePayload = { +// simplydata: { +// error: false, +// message: "Login successful", +// apiversion: fastify.config ? fastify.config.APIVERSION : undefined, +// access_token: token, +// buildingName: user.buildingName, +// email: user.emails, +// phone: user.phone, +// customerId: user.customerId, +// username: user.username, +// address1: user.profile.address1, +// address2: user.profile.address2, +// phoneVerified: user.phoneVerified, +// oneTimePasswordSetFlag: user.oneTimePasswordSetFlag, +// latitude: user.latitude, +// longitude: user.longitude, +// type: user.profile.role, +// loginType: "user" +// } +// }; + +// if (profilePicture) { +// responsePayload.simplydata.picture = profilePicture.picture; +// } + +// return reply.send(responsePayload); + +// } catch (error) { +// console.error("Login Error:", error); +// return reply.code(500).send({ simplydata: { error: true, message: "Internal server error" } }); +// } +// } +// });