diff --git a/README.md b/README.md index 979731f50..76c5f510e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ BlueWave uptime monitoring application - POST [/api/v1/auth/register](#post-register) - POST [/api/v1/auth/login](#post-login) - POST [/api/v1/auth/user/{userId}](#post-auth-user-edit-id) + - POST [/api/v1/auth/recovery/request](#post-auth-recovery-request-id) + - POST [/api/v1/auth/recovery/validate](#post-auth-recovery-validate-id) + - POST [/api/v1/auth/recovery/reset](#post-auth-recovery-reset-id) ###### Monitors - GET [/api/v1/monitors](#get-monitors) - GET [/api/v1/monitor/{id}](#get-monitor-id) @@ -382,6 +385,166 @@ curl --request POST \ +
+POST/api/v1/auth/recovery/request + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ----- | -------- | ------------ | +> | email | `string` | User's email | + +###### Response Payload + +> | Type | Notes | +> | --------------- | --------------------------------------- | +> | `RecoveryToken` | Returns a recovery token if email found | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/request \ + --header 'Content-Type: application/json' \ + --data '{ + "email" : "ajhollid@gmail.com" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Created recovery token", + "data": { + "email": "your_email@gmail.com", + "token": "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193", + "_id": "6668878263587f30748e968e", + "expiry": "2024-06-11T17:21:06.984Z", + "createdAt": "2024-06-11T17:21:06.985Z", + "updatedAt": "2024-06-11T17:21:06.985Z", + "__v": 0 + } +} +``` + +
+ +
+POST/api/v1/auth/recovery/validate + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | ----------------------------------- | +> | recoveryToken | `string` | Token issued in `/recovery/request` | + +###### Response Payload + +> | Type | Notes | +> | --------------- | -------------------------- | +> | `RecoveryToken` | Returns the recovery token | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/validate \ + --header 'Content-Type: application/json' \ + --data '{ + "recoveryToken" : "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Token is valid", + "data": { + "_id": "6668894263587f30748e969a", + "email": "ajhollid@gmail.com", + "token": "457d9926b24dedf613f120eeb524ef00ac45b3f0fc5c70bd25b1cc8aa83a64a0", + "expiry": "2024-06-11T17:28:34.349Z", + "createdAt": "2024-06-11T17:28:34.349Z", + "updatedAt": "2024-06-11T17:28:34.349Z", + "__v": 0 + } +} +``` + +
+ +
+POST/api/v1/auth/recovery/reset + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | --------------------------------------------- | +> | recoveryToken | `string` | Token issued returned by `/recovery/validate` | +> | password | `string` | User's new password` | + +###### Response Payload + +> | Type | Notes | +> | ------ | ------------------------ | +> | `User` | Returns the updated user | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/reset \ + --header 'Content-Type: application/json' \ + --data '{ + "recoveryToken" : "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193", + "password": "testtest" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Password reset", + "data": { + "_id": "66675891cb17336d84c25d9f", + "firstname": "User First Name", + "lastname": "User Last Name", + "email": "your_email@gmail.com", + "isActive": true, + "isVerified": false, + "createdAt": "2024-06-10T19:48:33.863Z", + "updatedAt": "2024-06-11T17:21:22.289Z", + "__v": 0 + } +} +``` + +
+ --- ###### Monitors diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 52e7b4bdc..6c532e565 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -154,15 +154,13 @@ const userEditController = async (req, res, next) => { const recoveryRequestController = async (req, res, next) => { try { - const email = req.body.email; const user = await req.db.getUserByEmail(req, res); 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, + data: recoveryToken, }); } // TODO Email token to user @@ -175,8 +173,8 @@ const recoveryRequestController = async (req, res, next) => { const validateRecoveryTokenController = async (req, res, next) => { try { - const candidateToken = req.body.recoveryToken; const recoveryToken = await req.db.validateRecoveryToken(req, res); + // TODO Redirect user to reset password after validating token return res.status(200).json({ success: true, msg: "Token is valid", @@ -188,10 +186,21 @@ const validateRecoveryTokenController = async (req, res, next) => { return; } }; + +const resetPasswordController = async (req, res, next) => { + try { + user = await req.db.resetPassword(req, res); + res.status(200).json({ success: true, msg: "Password reset", data: user }); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; module.exports = { registerController, loginController, userEditController, recoveryRequestController, validateRecoveryTokenController, + resetPasswordController, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 781d4309a..f64059342 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -3,7 +3,7 @@ const mongoose = require("mongoose"); const UserModel = require("../models/user"); const Check = require("../models/Check"); const Alert = require("../models/Alert"); -const RecoveyToken = require("../models/RecoveryToken"); +const RecoveryToken = require("../models/RecoveryToken"); const crypto = require("crypto"); const connect = async () => { @@ -100,8 +100,8 @@ const updateUser = async (req, res) => { const requestRecoveryToken = async (req, res) => { try { // Delete any existing tokens - await RecoveyToken.deleteMany({ email: req.body.email }); - let recoveryToken = new RecoveyToken({ + await RecoveryToken.deleteMany({ email: req.body.email }); + let recoveryToken = new RecoveryToken({ email: req.body.email, token: crypto.randomBytes(32).toString("hex"), }); @@ -115,7 +115,9 @@ const requestRecoveryToken = async (req, res) => { const validateRecoveryToken = async (req, res) => { try { const candidateToken = req.body.recoveryToken; - const recoveryToken = await RecoveyToken.findOne({ token: candidateToken }); + const recoveryToken = await RecoveryToken.findOne({ + token: candidateToken, + }); if (recoveryToken !== null) { return recoveryToken; } else { @@ -126,6 +128,29 @@ const validateRecoveryToken = async (req, res) => { } }; +const resetPassword = async (req, res) => { + try { + const newPassword = req.body.password; + + // Validate token again + const recoveryToken = await validateRecoveryToken(req, res); + const user = await UserModel.findOne({ email: recoveryToken.email }); + + if (user !== null) { + user.password = newPassword; + await user.save(); + await RecoveryToken.deleteMany({ email: recoveryToken.email }); + // Fetch the user again without the password + const userWithoutPassword = await UserModel.findOne({ + email: recoveryToken.email, + }).select("-password"); + return userWithoutPassword; + } else { + throw new Error("User not found"); + } + } catch (error) {} +}; + //**************************************** // Monitors //**************************************** @@ -435,6 +460,7 @@ module.exports = { updateUser, requestRecoveryToken, validateRecoveryToken, + resetPassword, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index f300edfa9..1049659fa 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -7,15 +7,17 @@ const { userEditController, recoveryRequestController, validateRecoveryTokenController, + resetPasswordController, } = require("../controllers/authController"); +//Auth routes router.post("/register", registerController); router.post("/login", loginController); router.post("/user/:userId", verifyJWT, userEditController); //Recovery routes - router.post("/recovery/request", recoveryRequestController); router.post("/recovery/validate", validateRecoveryTokenController); +router.post("/recovery/reset/", resetPasswordController); module.exports = router;