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;