Untitled
unknown
plain_text
2 years ago
6.7 kB
12
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