From 276dadab663aed4fd9375e3b9f2617a3995891e6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Jun 2024 15:54:45 -0700 Subject: [PATCH] Added recovery Request route, controller, and DB operations --- Server/controllers/authController.js | 34 +++++++++++++++++++----- Server/db/MongoDB.js | 39 +++++++++++++++++++++++----- Server/models/RecoveryToken.js | 25 ++++++++++++++++++ Server/routes/authRoute.js | 5 ++++ Server/service/jobQueue.js | 1 - 5 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 Server/models/RecoveryToken.js diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 8c242af67..dae68771f 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -13,6 +13,7 @@ const { sendEmail } = require("../utils/sendEmail"); const { registerTemplate, } = require("../utils/emailTemplates/registerTemplate"); +const RecoveryToken = require("../models/RecoveryToken"); /** * Creates and returns JWT token with an arbitrary payload @@ -97,10 +98,6 @@ const loginController = async (req, res, next) => { // Check if user exists const user = await req.db.getUserByEmail(req, res); - // If user not found, throw an error - if (!user) { - throw new Error("User not found!"); - } // Compare password const match = await user.comparePassword(req.body.password); if (!match) { @@ -145,7 +142,7 @@ const userEditController = async (req, res, next) => { try { const updatedUser = await req.db.updateUser(req, res); - res + return res .status(200) .json({ success: true, msg: "User updated", data: updatedUser }); } catch (error) { @@ -155,4 +152,29 @@ const userEditController = async (req, res, next) => { } }; -module.exports = { registerController, loginController, userEditController }; +const recoveryRequestController = async (req, res, next) => { + const email = req.body.email; + try { + const user = await req.db.getUserByEmail(req, res); + // TODO Email token to user + if (user) { + const recoveryToken = await req.db.requestRecoveryToken(req, res); + console.log(recoveryToken); + return res.status(200).json({ + success: true, + msg: "Created recovery token", + data: recoveryToken.token, + }); + } + } catch (error) { + error.service = SERVICE_NAME; + next(error); + return; + } +}; +module.exports = { + registerController, + loginController, + userEditController, + recoveryRequestController, +}; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index aabd3c384..54e6e2c0a 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -3,10 +3,8 @@ const mongoose = require("mongoose"); const UserModel = require("../models/user"); const Check = require("../models/Check"); const Alert = require("../models/Alert"); - -const verifyId = (userId, monitorId) => { - return userId.toString() === monitorId.toString(); -}; +const RecoveyToken = require("../models/RecoveryToken"); +const crypto = require("crypto"); const connect = async () => { try { @@ -44,6 +42,7 @@ const insertUser = async (req, res) => { * Get User by Email * Gets a user by Email. Not sure if we'll ever need this except for login. * If not needed except for login, we can move password comparison here + * Throws error if user not found * @async * @param {Express.Request} req * @param {Express.Response} res @@ -52,10 +51,14 @@ const insertUser = async (req, res) => { */ const getUserByEmail = async (req, res) => { try { - // Returns null if no user is found // Need the password to be able to compare, removed .select() // We can strip the hash before returing the user - return await UserModel.findOne({ email: req.body.email }); + const user = await UserModel.findOne({ email: req.body.email }); + if (user) { + return user; + } else { + throw new Error("User not found"); + } } catch (error) { throw error; } @@ -86,6 +89,29 @@ const updateUser = async (req, res) => { } }; +/** + * Request a recovery token + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const requestRecoveryToken = async (req, res) => { + try { + // Delete any existing tokens + await RecoveyToken.deleteMany({ email: req.body.email }); + let recoveryToken = new RecoveyToken({ + email: req.body.email, + token: crypto.randomBytes(32).toString("hex"), + }); + await recoveryToken.save(); + return recoveryToken; + } catch (error) { + throw error; + } +}; + //**************************************** // Monitors //**************************************** @@ -393,6 +419,7 @@ module.exports = { insertUser, getUserByEmail, updateUser, + requestRecoveryToken, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/models/RecoveryToken.js b/Server/models/RecoveryToken.js new file mode 100644 index 000000000..2b67a89e1 --- /dev/null +++ b/Server/models/RecoveryToken.js @@ -0,0 +1,25 @@ +const mongoose = require("mongoose"); + +const RecoveryTokenSchema = mongoose.Schema( + { + email: { + type: String, + required: true, + unique: true, + }, + token: { + type: String, + required: true, + }, + expiry: { + type: Date, + default: Date.now, + expires: 600, + }, + }, + { + timestamps: true, + } +); + +module.exports = mongoose.model("RecoveryToken", RecoveryTokenSchema); diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 637e4bf5e..41772c49b 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -5,10 +5,15 @@ const { registerController, loginController, userEditController, + recoveryRequestController, } = require("../controllers/authController"); router.post("/register", registerController); router.post("/login", loginController); router.post("/user/:userId", verifyJWT, userEditController); +//Recovery routes + +router.post("/recovery/request", recoveryRequestController); + module.exports = router; diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 84241f443..5a9fbc6d5 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -14,7 +14,6 @@ class JobQueue { * @throws {Error} */ constructor() { - console.log(process.env.REDIS_PORT); this.queue = new Queue(QUEUE_NAME, { connection, });