From 7c05dc30e04cf35c4bff57b6d5f48049c13f1b31 Mon Sep 17 00:00:00 2001 From: Louis Date: Fri, 8 Nov 2024 22:49:52 +0100 Subject: [PATCH] handleLogin vulnerablity closes #23 --- src/controller/auth.controller.ts | 126 ++++++++++++++++++------------ 1 file changed, 74 insertions(+), 52 deletions(-) diff --git a/src/controller/auth.controller.ts b/src/controller/auth.controller.ts index b906532..4c278ed 100644 --- a/src/controller/auth.controller.ts +++ b/src/controller/auth.controller.ts @@ -132,70 +132,92 @@ export const handleLogin = async ( }); } + // Generating the dummy hash dynamically may introduce a slight performance overhead, as argon2.hash() + // is a computationally expensive operation. + // However, this is generally negligible compared to the security benefits it provides + const dummyPassword = 'dummy_password'; + const dummyHash = await argon2.hash(dummyPassword); + + // Use the user's hash if found, otherwise use the dummy hash + const userPasswordHash = user ? user.password : dummyHash; + // check password try { - if (await argon2.verify(user.password, password)) { - // if there is a refresh token in the req.cookie, then we need to check if this - // refresh token exists in the database and belongs to the current user than we need to delete it - // if the token does not belong to the current user, then we delete all refresh tokens - // of the user stored in the db to be on the safe site - // we also clear the cookie in both cases - if (cookies?.[config.jwt.refresh_token.cookie_name]) { - // check if the given refresh token is from the current user - const checkRefreshToken = await prismaClient.refreshToken.findUnique({ + const isPasswordValid = await argon2.verify(userPasswordHash, password); + + // Check if email is verified + // Check for verified email after verifying the password to prevent user enumeration attacks + if (!user.emailVerified) { + return res.status(httpStatus.UNAUTHORIZED).json({ + message: 'Your email is not verified! Please confirm your email!' + }); + } + + // If password is invalid, return unauthorized + if (!isPasswordValid) { + return res.status(httpStatus.UNAUTHORIZED).json({ + message: 'Invalid email or password!' + }); + } + + // if there is a refresh token in the req.cookie, then we need to check if this + // refresh token exists in the database and belongs to the current user than we need to delete it + // if the token does not belong to the current user, then we delete all refresh tokens + // of the user stored in the db to be on the safe site + // we also clear the cookie in both cases + if (cookies?.[config.jwt.refresh_token.cookie_name]) { + // check if the given refresh token is from the current user + const checkRefreshToken = await prismaClient.refreshToken.findUnique({ + where: { + token: cookies[config.jwt.refresh_token.cookie_name] + } + }); + + // if this token does not exists int the database or belongs to another user, + // then we clear all refresh tokens from the user in the db + if (!checkRefreshToken || checkRefreshToken.userId !== user.id) { + await prismaClient.refreshToken.deleteMany({ + where: { + userId: user.id + } + }); + } else { + // else everything is fine and we just need to delete the one token + await prismaClient.refreshToken.delete({ where: { token: cookies[config.jwt.refresh_token.cookie_name] } }); - - // if this token does not exists int the database or belongs to another user, - // then we clear all refresh tokens from the user in the db - if (!checkRefreshToken || checkRefreshToken.userId !== user.id) { - await prismaClient.refreshToken.deleteMany({ - where: { - userId: user.id - } - }); - } else { - // else everything is fine and we just need to delete the one token - await prismaClient.refreshToken.delete({ - where: { - token: cookies[config.jwt.refresh_token.cookie_name] - } - }); - } - - // also clear the refresh token in the cookie - res.clearCookie( - config.jwt.refresh_token.cookie_name, - clearRefreshTokenCookieConfig - ); } - const accessToken = createAccessToken(user.id); + // also clear the refresh token in the cookie + res.clearCookie( + config.jwt.refresh_token.cookie_name, + clearRefreshTokenCookieConfig + ); + } - const newRefreshToken = createRefreshToken(user.id); + const accessToken = createAccessToken(user.id); - // store new refresh token in db - await prismaClient.refreshToken.create({ - data: { - token: newRefreshToken, - userId: user.id - } - }); + const newRefreshToken = createRefreshToken(user.id); - // save refresh token in cookie - res.cookie( - config.jwt.refresh_token.cookie_name, - newRefreshToken, - refreshTokenCookieConfig - ); + // store new refresh token in db + await prismaClient.refreshToken.create({ + data: { + token: newRefreshToken, + userId: user.id + } + }); - // send access token per json to user so it can be stored in the localStorage - return res.json({ accessToken }); - } else { - return res.status(httpStatus.UNAUTHORIZED); - } + // save refresh token in cookie + res.cookie( + config.jwt.refresh_token.cookie_name, + newRefreshToken, + refreshTokenCookieConfig + ); + + // send access token per json to user so it can be stored in the localStorage + return res.json({ accessToken }); } catch (err) { return res.status(httpStatus.INTERNAL_SERVER_ERROR); }