Merge pull request #585 from bluewave-labs/feat/login-security
Feat/login security, resolves #583
This commit is contained in:
7 files changed
+70
-9
No files matched your search
@@ -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");
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -40,6 +40,9 @@ const UserSchema = mongoose.Schema(
|
||||
default: "user",
|
||||
enum: ["user", "admin"],
|
||||
},
|
||||
authToken: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user