Merge pull request #585 from bluewave-labs/feat/login-security

Feat/login security, resolves #583
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-08-10 10:00:10 -07:00
commit 14f6f05dfc
7 files changed
+70 -9

No files matched your search

+14 -1
View File
@@ -21,6 +21,8 @@ import UserSvg from "../../assets/icons/user.svg?react";
import TeamSvg from "../../assets/icons/user-two.svg?react";
import LogoutSvg from "../../assets/icons/logout.svg?react";
import { Stack, useScrollTrigger } from "@mui/material";
import axiosIntance from "../../Utils/axiosConfig";
import axios from "axios";
const icons = {
Profile: <UserSvg />,
@@ -93,10 +95,21 @@ function NavBar() {
* Handles logging out the user
*
*/
const logout = () => {
const logout = async () => {
// Clear auth state
dispatch(clearAuthState());
dispatch(clearUptimeMonitorState());
// Make request to BE to remove JWT from user
await axiosIntance.post(
"/auth/logout",
{ user: authState.user },
{
headers: {
Authorization: `Bearer ${authState.authToken}`,
"Content-Type": "application/json",
},
}
);
navigate("/login");
};
+27
View File
@@ -135,6 +135,10 @@ const loginController = async (req, res, next) => {
throw new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
}
if (user.authToken) {
throw new Error(errorMessages.AUTH_ALREADY_LOGGED_IN);
}
// Remove password from user object. Should this be abstracted to DB layer?
const userWithoutPassword = { ...user._doc };
delete userWithoutPassword.password;
@@ -142,6 +146,8 @@ const loginController = async (req, res, next) => {
// Happy path, return token
const token = issueToken(userWithoutPassword);
user.authToken = token;
await user.save();
// reset avatar image
userWithoutPassword.avatarImage = user.avatarImage;
@@ -157,6 +163,26 @@ const loginController = async (req, res, next) => {
}
};
const logoutController = async (req, res, next) => {
try {
// Get user
const { user } = req.body;
const userToLogout = await req.db.getUserByEmail(
{ body: { email: user.email } },
res
);
userToLogout.authToken = null;
await userToLogout.save();
return res
.status(200)
.json({ success: true, msg: successMessages.AUTH_LOGOUT_USER });
} catch (error) {
error.service = SERVICE_NAME;
next(error);
}
};
const userEditController = async (req, res, next) => {
try {
await editUserParamValidation.validateAsync(req.params);
@@ -524,6 +550,7 @@ const getAllUsersController = async (req, res) => {
module.exports = {
registerController,
loginController,
logoutController,
userEditController,
inviteController,
inviteVerifyController,
-1
View File
@@ -27,7 +27,6 @@ const insertUser = async (req, res) => {
const avatar = await GenerateAvatarImage(req.file);
userData.avatarImage = avatar;
}
console.log(userData);
const newUser = new UserModel(userData);
await newUser.save();
return await UserModel.findOne({ _id: newUser._id })
+22 -7
View File
@@ -3,6 +3,8 @@ const logger = require("../utils/logger");
const SERVICE_NAME = "verifyJWT";
const TOKEN_PREFIX = "Bearer ";
const { errorMessages } = require("../utils/messages");
const { parse } = require("path");
const User = require("../models/user");
/**
* Verifies the JWT token
* @function
@@ -32,14 +34,27 @@ const verifyJWT = (req, res, next) => {
const parsedToken = token.slice(TOKEN_PREFIX.length, token.length);
// Verify the token's authenticity
jwt.verify(parsedToken, process.env.JWT_SECRET, (err, decoded) => {
jwt.verify(parsedToken, process.env.JWT_SECRET, async (err, decoded) => {
if (err) {
logger.error(errorMessages.INVALID_AUTH_TOKEN, {
service: SERVICE_NAME,
});
return res
.status(401)
.json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN });
try {
const userId = jwt.decode(parsedToken)._id;
await User.updateOne({ _id: userId }, { $unset: { authToken: null } });
logger.error(errorMessages.INVALID_AUTH_TOKEN, {
service: SERVICE_NAME,
});
return res
.status(401)
.json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN });
} catch (error) {
logger.error(errorMessages.UNKNOWN_ERROR, {
service: SERVICE_NAME,
error: error,
});
error.status = 401;
error.service = SERVICE_NAME;
next(error);
return;
}
}
//Add the user to the request object for use in the route
req.user = decoded;
+3
View File
@@ -40,6 +40,9 @@ const UserSchema = mongoose.Schema(
default: "user",
enum: ["user", "admin"],
},
authToken: {
type: String,
},
},
{
timestamps: true,
+2
View File
@@ -9,6 +9,7 @@ const User = require("../models/user");
const {
registerController,
loginController,
logoutController,
userEditController,
recoveryRequestController,
validateRecoveryTokenController,
@@ -23,6 +24,7 @@ const {
//Auth routes
router.post("/register", upload.single("profileImage"), registerController);
router.post("/login", loginController);
router.post("/logout", logoutController);
router.put(
"/user/:userId",
upload.single("profileImage"),
+2
View File
@@ -7,6 +7,7 @@ const errorMessages = {
UNAUTHORIZED: "Unauthorized access",
AUTH_ADMIN_EXISTS: "Admin already exists",
AUTH_INVITE_NOT_FOUND: "Invite not found",
AUTH_ALREADY_LOGGED_IN: "User already logged in",
//Error handling middleware
UNKNOWN_SERVICE: "Unknown service",
@@ -55,6 +56,7 @@ const successMessages = {
// Auth Controller
AUTH_CREATE_USER: "User created successfully",
AUTH_LOGIN_USER: "User logged in successfully",
AUTH_LOGOUT_USER: "User logged out successfully",
AUTH_UPDATE_USER: "User updated successfully",
AUTH_CREATE_RECOVERY_TOKEN: "Recovery token created successfully",
AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully",