Untitled
unknown
plain_text
a year ago
6.7 kB
6
Indexable
import React from "react"; import Users from "@/models/users"; import CryptoJS from "crypto-js"; import jwt, { Secret } from "jsonwebtoken"; import { Request, Response } from "express"; import dayjs from "dayjs"; import { render } from "@react-email/render"; import { REQUIRED_VALUE_EMPTY, UNKNOWN_ERROR_OCCURRED, } from "@/common/constants"; import { encryptKey, signKey } from "@/common/config"; import OtpEmailTemplate from "@/common/templates/OtpEmailTemplate"; import { sendOtpEmail } from "@/common/utils/sendOtpEmail"; import { sendOtpSms } from "@/common/utils/sendOtpSMS"; // Function to render the email to HTML const renderOtpEmail = (otp: string) => { return render(React.createElement(OtpEmailTemplate, { otp })); }; const IS_DEV_MODE = process.env.NODE_ENV === "development"; export const login = async (req: Request, res: Response) => { const { email, password, rememberMe } = req.body; if (!email || !password) { return res.status(400).json({ error: true, message: REQUIRED_VALUE_EMPTY, }); } try { const user = await Users.findOne({ email }); if (!user || user.deletedAt) { throw new Error("Account does not exist in our system"); } if (user.blockedAt) { throw new Error("Account was prohibited to login due to violations"); } const decryptPassword = CryptoJS.AES.decrypt( user.password as string, encryptKey as string ); const originalPassword = decryptPassword.toString(CryptoJS.enc.Utf8); if (originalPassword !== password) { throw new Error("Email or password is invalid"); } if (IS_DEV_MODE) { const token = jwt.sign( { _id: user._id, email: user.email, role: user.role, rememberMe: rememberMe }, signKey as string, { expiresIn: "1d" } ); return res.json({ error: false, message: null, item: token, itemCount: null, }); } // If not in development mode, proceed with OTP. // Correct password, generate OTP const otp = Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit OTP user.otp = otp; user.otpExpiresAt = new Date(Date.now() + 10 * 60000); // OTP expires in 10 minutes await user.save(); return res.json({ error: false, otpRequired: true, methodSelectionRequired: true, message: "OTP sent to email. Please verify to continue.", }); } catch (err: any) { const message = err.message || UNKNOWN_ERROR_OCCURRED; return res.status(500).json({ error: true, message: message, }); } }; export const verify = async (req: Request, res: Response) => { const bearerHeader = req.headers["authorization"]; if (bearerHeader) { const bearer = bearerHeader.split(" "); const bearerToken = bearer[1]; try { const { _id, email, expiresIn, rememberMe }: any = jwt.verify( bearerToken as string, signKey as Secret ); const user = await Users.findOne({ email, }).populate('organisation') const isTokenExpired = dayjs().isAfter(expiresIn); if (user && !isTokenExpired) { res.json({ error: false, message: null, item: { token: req.params.token, _id: user._id, email: user.email, role: user.role, organisation: user.organisation }, }); } else { if(rememberMe) { const token = jwt.sign( { _id: user._id, email: user.email, role: user.role, rememberMe: rememberMe }, signKey as string, { expiresIn: "1d" } ); res.json({ error: false, message: null, item: { token: token, _id: user._id, email: user.email, role: user.role, organisation: user.organisation }, }); } else { res.json({ error: true, message: "Authentication is expired or invalid", items: null, itemCount: null, }); } } } catch (error) { res.json({ error: true, message: String(error), items: null, itemCount: null, }); } } else { res.json({ error: true, message: REQUIRED_VALUE_EMPTY, items: null, itemCount: null, }); } }; export const verifyOtp = async (req: Request, res: Response) => { const { email, otp } = req.body; try { const user = await Users.findOne({ email, otp, otpExpiresAt: { $gt: Date.now() }, }); if (!user) { return res.json({ error: true, message: "OTP is invalid or expired", }); } // OTP is correct, and not expired user.otp = ""; // clear OTP user.otpExpiresAt = undefined; // clear OTP expiry await user.save(); // Now create the JWT token since OTP is verified const token = jwt.sign( { id: user._id, email: user.email, role: user.role }, signKey as string, { expiresIn: "1d" } ); // Send a success response return res.json({ error: false, message: "OTP verified, user logged in successfully", item: token, itemCount: null, }); } catch (error) { // Catch any other errors and respond accordingly return res.json({ error: true, message: String(error), items: null, itemCount: null, }); } }; export const selectOtpMethod = async (req: Request, res: Response) => { const { email, method } = req.body; try { const user = await Users.findOne({ email }); if (!user) { throw new Error("User not found"); } // Check if OTP is available const otp = user.otp; if (!otp) { throw new Error("OTP not found or expired"); } if (method === "sms") { // Check if phone number is available if (!user.phoneNumber) { throw new Error("Phone number not available"); } await sendOtpSms(user.phoneNumber, otp); } else { const htmlEmailContent = renderOtpEmail(otp); await sendOtpEmail(email, htmlEmailContent); } return res.json({ error: false, message: `OTP sent via ${method}. Please verify to continue.`, }); } catch (error) { return res.status(500).json({ error: true, message: String(error), }); } };
Editor is loading...
Leave a Comment