From 09e05bff7c32f43c7f792fe77ffdf67750dcd697 Mon Sep 17 00:00:00 2001 From: Balaharisankar Lakshmanaperumal <114602603+BHS-Harish@users.noreply.github.com> Date: Sat, 10 Aug 2024 18:10:21 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=B8=20Feature=20:=20Keep=20me=20logged?= =?UTF-8?q?=20In=20feature=20added=20(#1129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upvote and Downvotes fixed * Keep me logged in feature added --- backend/app/models/Admin.js | 4 +++ backend/app/routes/admin/changePassword.js | 2 +- backend/app/routes/admin/getAdmins.js | 15 ++++++-- backend/app/routes/admin/resetPassword.js | 2 +- .../routes/auth/@validationSchema/index.js | 1 + backend/app/routes/auth/login.js | 7 ++-- backend/helpers/middlewares/auth.js | 3 +- .../Setting/ResetPassword/ResetPassword.jsx | 4 +++ frontend/src/pages/Login/Login.jsx | 36 ++++++++++++++++--- frontend/src/pages/Login/login.module.scss | 7 ++++ 10 files changed, 68 insertions(+), 13 deletions(-) diff --git a/backend/app/models/Admin.js b/backend/app/models/Admin.js index e3d0478f..716327aa 100644 --- a/backend/app/models/Admin.js +++ b/backend/app/models/Admin.js @@ -37,6 +37,10 @@ const adminSchema = new Schema( type: String, trim: true, }, + refreshToken:{ + type:String, + trim:true + } }, { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } ); diff --git a/backend/app/routes/admin/changePassword.js b/backend/app/routes/admin/changePassword.js index 02735c5f..fdf2ffa4 100644 --- a/backend/app/routes/admin/changePassword.js +++ b/backend/app/routes/admin/changePassword.js @@ -36,7 +36,7 @@ module.exports = async (req, res, next) => { const hashedPassword = await argon2.hash(newPassword); const [err] = await to( - Admin.findOneAndUpdate({ email: userRecord.email }, { $set: { passwordHash: hashedPassword } }) + Admin.findOneAndUpdate({ email: userRecord.email }, { $set: { passwordHash: hashedPassword,refreshToken:"" } }) ); if (err) { diff --git a/backend/app/routes/admin/getAdmins.js b/backend/app/routes/admin/getAdmins.js index 8aebb616..fd9a5295 100644 --- a/backend/app/routes/admin/getAdmins.js +++ b/backend/app/routes/admin/getAdmins.js @@ -2,6 +2,7 @@ const to = require('await-to-js').default; const Admin = require('../../models/Admin'); const { ErrorHandler } = require('../../../helpers/error'); const constants = require('../../../constants'); +const { getTokenFromHeader } = require('../../../helpers/middlewares/auth') const getAdminsAggregate = (match, page) => { const pipeline = [ @@ -15,7 +16,7 @@ const getAdminsAggregate = (match, page) => { email: 1, contact: 1, isSuperAdmin: 1, - image:1 + image: 1 }, }, { $skip: constants.PAGINATION_LIMIT.GET_ADMINS * (Number(page) - 1) }, @@ -37,10 +38,20 @@ module.exports = async (req, res, next) => { email: req.query.email || '', }; } + const token = await getTokenFromHeader(req) const [err, response] = await to(Admin.aggregate(getAdminsAggregate(match, page))); if (err) { const error = new ErrorHandler(constants.ERRORS.DATABASE, { - statusCode: '500', + statusCode: 500, + message: 'The server encountered an unexpected condition which prevented it from fulfilling the request.', + errStack: err, + }); + return next(error); + } + const refreshToken = await Admin.findOne({ email: response[0].email }) + if (token != refreshToken?.refreshToken) { + const error = new ErrorHandler(constants.ERRORS.DATABASE, { + statusCode: 500, message: 'The server encountered an unexpected condition which prevented it from fulfilling the request.', errStack: err, }); diff --git a/backend/app/routes/admin/resetPassword.js b/backend/app/routes/admin/resetPassword.js index a6db3a0c..dfa9e41e 100644 --- a/backend/app/routes/admin/resetPassword.js +++ b/backend/app/routes/admin/resetPassword.js @@ -31,7 +31,7 @@ module.exports = async (req, res, next) => { const hashedPassword = await argon2.hash(newPassword); // Finding and updating the admin password - const [err] = await to(Admin.findOneAndUpdate({ email }, { passwordHash: hashedPassword }, { new: true })); + const [err] = await to(Admin.findOneAndUpdate({ email }, { passwordHash: hashedPassword,refreshToken:"" }, { new: true })); // Throwing error in admin not found if (err) { diff --git a/backend/app/routes/auth/@validationSchema/index.js b/backend/app/routes/auth/@validationSchema/index.js index b2106529..f6e042b8 100644 --- a/backend/app/routes/auth/@validationSchema/index.js +++ b/backend/app/routes/auth/@validationSchema/index.js @@ -3,6 +3,7 @@ const Joi = require('joi'); const loginSchema = Joi.object().keys({ email: Joi.string().required(), password: Joi.string().required(), + keepMeLoggedIn:Joi.boolean() }); module.exports = loginSchema; diff --git a/backend/app/routes/auth/login.js b/backend/app/routes/auth/login.js index 57001a07..dc1b73ea 100644 --- a/backend/app/routes/auth/login.js +++ b/backend/app/routes/auth/login.js @@ -2,10 +2,10 @@ const argon2 = require('argon2'); const Admin = require('../../models/Admin'); const { ErrorHandler } = require('../../../helpers/error'); const constants = require('../../../constants'); -const { generateJWT } = require('../../../helpers/middlewares/auth'); +const { generateJWT,generateJWTWithOutExpire } = require('../../../helpers/middlewares/auth'); module.exports = async (req, res, next) => { - const { email, password } = req.body; + const { email, password,keepMeLoggedIn } = req.body; const userRecord = await Admin.findOne({ email }); if (!userRecord) { const error = new ErrorHandler(constants.ERRORS.INPUT, { @@ -34,7 +34,8 @@ module.exports = async (req, res, next) => { isSuperAdmin: userRecord.isSuperAdmin, phone: userRecord.contact, }; - const JWT = generateJWT(JWTPayload); + const JWT = keepMeLoggedIn?generateJWTWithOutExpire(JWTPayload):generateJWT(JWTPayload); + const updateRefreshToken=await Admin.findByIdAndUpdate(userRecord.id,{refreshToken:JWT}) const response = { ...JWTPayload, token: JWT }; return res.status(200).send(response); }; diff --git a/backend/helpers/middlewares/auth.js b/backend/helpers/middlewares/auth.js index fa38cfc3..a2a545c7 100644 --- a/backend/helpers/middlewares/auth.js +++ b/backend/helpers/middlewares/auth.js @@ -6,6 +6,7 @@ const constants = require('../../constants'); const generateJWT = (payload, expiry = config.JWT_EXPIRES_IN) => sign(payload, config.JWT_SECRET_KEY, { expiresIn: expiry }); +const generateJWTWithOutExpire = (payload) => sign(payload, config.JWT_SECRET_KEY) const getTokenFromHeader = async (req) => { if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { return req.headers.authorization.split(' ')[1]; @@ -38,4 +39,4 @@ const authMiddleware = async (req, res, next) => { next(); }; -module.exports = { authMiddleware, generateJWT, verifyToken }; +module.exports = { authMiddleware, generateJWT, verifyToken,generateJWTWithOutExpire ,getTokenFromHeader}; diff --git a/frontend/src/pages/Admin/Components/Setting/ResetPassword/ResetPassword.jsx b/frontend/src/pages/Admin/Components/Setting/ResetPassword/ResetPassword.jsx index 3312a3b9..5ab0801c 100644 --- a/frontend/src/pages/Admin/Components/Setting/ResetPassword/ResetPassword.jsx +++ b/frontend/src/pages/Admin/Components/Setting/ResetPassword/ResetPassword.jsx @@ -4,6 +4,8 @@ import { Button2 } from "../../../../../components/util/Button"; import { END_POINT } from "./../../../../../config/api"; import { SimpleToast } from "./../../../../../components/util/Toast/Toast"; import style from "./reset-password.module.scss"; +import { useDispatch } from "react-redux"; +import { logout } from "../../../../../store/actions/auth"; export function ResetPassword() { const [oldPassword, setOldPassword] = useState(""); @@ -16,6 +18,7 @@ export function ResetPassword() { const oldPasswordInput = useRef("oldpassword"); const newPasswordInput = useRef("newpassword"); const confirmPasswordInput = useRef("confirmpassword"); + const dispatch = useDispatch(); const token = useSelector((state) => state.token); @@ -63,6 +66,7 @@ export function ResetPassword() { if (response.status === 200) { setOpenSuccessToast(true); setPasswordChange(true); + logout(dispatch); } response .json() diff --git a/frontend/src/pages/Login/Login.jsx b/frontend/src/pages/Login/Login.jsx index 72e7577d..6459c407 100644 --- a/frontend/src/pages/Login/Login.jsx +++ b/frontend/src/pages/Login/Login.jsx @@ -1,5 +1,6 @@ import React, { useState, useRef, useEffect } from "react"; import { Button2 } from "../../components/util/Button/index"; +import { Checkbox } from "@material-ui/core"; import style from "./login.module.scss"; import { useDispatch, useSelector } from "react-redux"; import * as actions from "../../store/actions/actions"; @@ -17,10 +18,11 @@ export function Login(props) { const dispatch = useDispatch(); const dark = props.theme; const [errorObj, setErrorObj] = useState({}); - const [isLoading,setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(false); const validationSchema = { email: Joi.string().email().required(), password: Joi.string().required(), + keepMeLoggedIn: Joi.boolean(), }; const isFormValid = () => { @@ -105,7 +107,7 @@ export function Login(props) { .json() .then((res) => { if (response.status === 200) { - const firstName = res.name.split(' ')[0]; + const firstName = res.name.split(" ")[0]; localStorage.setItem("token", res.token); localStorage.setItem("isSuperAdmin", res.isSuperAdmin); localStorage.setItem("firstName", firstName); @@ -120,12 +122,14 @@ export function Login(props) { }) .catch((err) => { console.error(err); - setOpenError3Toast(true)}) + setOpenError3Toast(true); + }) ) .catch((err) => { setOpenError1Toast(true); console.error("must be a backend problem🤔:", err); - }).finally(()=> { + }) + .finally(() => { setIsLoading(false); }); } @@ -137,7 +141,9 @@ export function Login(props) { return ( <> -
{isLoading?:null}
+
+ {isLoading ? : null} +
+
+ { + setCredential({ + ...credential, + keepMeLoggedIn: e.target.checked, + }); + }} + /> + +