From 61b209a082c3c36a2196a71702b6824a634bba72 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Mon, 29 Sep 2025 00:13:52 +0530 Subject: [PATCH] addded remember me functionality --- src/hooks/useAuth.jsx | 40 ++--- src/pages/authentication/LoginPage.jsx | 79 +++++----- .../authentication/TenantSelectionPage.jsx | 2 +- src/router/ProtectedRoute.jsx | 138 ++++++++---------- src/utils/authUtils.js | 7 + src/utils/axiosClient.jsx | 18 ++- 6 files changed, 137 insertions(+), 147 deletions(-) diff --git a/src/hooks/useAuth.jsx b/src/hooks/useAuth.jsx index 8f158197..8e5b8de2 100644 --- a/src/hooks/useAuth.jsx +++ b/src/hooks/useAuth.jsx @@ -12,6 +12,7 @@ import { closeAuthModal, openAuthModal, } from "../slices/localVariablesSlice.jsx"; +import { removeSession } from "../utils/authUtils.js"; export const useTenants = () => { return useQuery({ @@ -28,18 +29,24 @@ export const useSelectTenant = (onSuccessCallBack) => { const res = await AuthRepository.selectTenant(tenantId); return res.data; }, - + onSuccess: (data) => { - localStorage.setItem("jwtToken", data.token); - localStorage.setItem("refreshToken", data.refreshToken); + if (localStorage.getItem("jwtToken")) { + localStorage.setItem("jwtToken", data.token); + localStorage.setItem("refreshToken", data.refreshToken); + } else { + sessionStorage.setItem("jwtToken", data.token); + sessionStorage.setItem("refreshToken", data.refreshToken); + } + if (onSuccessCallBack) onSuccessCallBack(); }, onError: (error) => { showToast(error.message || "Error while creating project", "error"); localStorage.removeItem("jwtToken"); - localStorage.removeItem("refreshToken") - localStorage.removeItem("ctnt") + localStorage.removeItem("refreshToken"); + localStorage.removeItem("ctnt"); }, }); }; @@ -55,29 +62,26 @@ export const useAuthModal = () => { }; }; -export const useLogout = ()=>{ + +export const useLogout = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async () => { - let payload = {refreshToken: localStorage.getItem("refreshToken")} - return await AuthRepository.logout(payload); + let payload = { refreshToken: localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken") }; + return await AuthRepository.logout(payload); }, - + onSuccess: (data) => { - localStorage.removeItem("jwtToken"); - localStorage.removeItem("refreshToken"); - localStorage.removeItem("ctnt"); - localStorage.clear(); - window.location.href = "/auth/login"; + removeSession() + + window.location.href = "/auth/login"; if (onSuccessCallBack) onSuccessCallBack(); }, onError: (error) => { showToast(error.message || "Error while creating project", "error"); - localStorage.removeItem("jwtToken"); - localStorage.removeItem("refreshToken") - localStorage.removeItem("ctnt") + removeSession() }, }); -} \ No newline at end of file +}; diff --git a/src/pages/authentication/LoginPage.jsx b/src/pages/authentication/LoginPage.jsx index 363de91f..6be7cb2b 100644 --- a/src/pages/authentication/LoginPage.jsx +++ b/src/pages/authentication/LoginPage.jsx @@ -16,13 +16,13 @@ const LoginPage = () => { const loginSchema = IsLoginWithOTP ? z.object({ - username: z.string().trim().email({ message: "Valid email required" }), - }) + username: z.string().trim().email({ message: "Valid email required" }), + }) : z.object({ - username: z.string().trim().email({ message: "Valid email required" }), - password: z.string().trim().min(1, { message: "Password required" }), - rememberMe: z.boolean(), - }); + username: z.string().trim().email({ message: "Valid email required" }), + password: z.string().trim().min(1, { message: "Password required" }), + rememberMe: z.boolean(), + }); const { register, @@ -41,8 +41,13 @@ const LoginPage = () => { password: data.password, }; const response = await AuthRepository.login(userCredential); - localStorage.setItem("jwtToken", response.data.token); - localStorage.setItem("refreshToken", response.data.refreshToken); + if (data.rememberMe) { + localStorage.setItem("jwtToken", response.data.token); + localStorage.setItem("refreshToken", response.data.refreshToken); + } else { + sessionStorage.setItem("jwtToken", response.data.token); + sessionStorage.setItem("refreshToken", response.data.refreshToken); + } setLoading(false); navigate("/auth/switch/org"); } else { @@ -69,6 +74,16 @@ const LoginPage = () => { } }, [IsLoginWithOTP]); + useEffect(() => { + const token = + localStorage.getItem("jwtToken") || + sessionStorage.getItem("jwtToken"); + + if (token) { + navigate("/dashboard", { replace: true }); + } +}, []); + return (
@@ -106,36 +121,6 @@ const LoginPage = () => { {/* Password */} {!IsLoginWithOTP && ( <> - {/*
- -
- - setHidepass(!hidepass)} - > - - -
- {errors.password && ( -
- {errors.password.message} -
- )} -
*/} -
- {/* ✅ Error message */} {errors.password && ( -
+
{errors.password.message}
)}
- {/* Remember Me + Forgot Password */}
@@ -209,8 +196,8 @@ const LoginPage = () => { {loading ? "Please Wait..." : IsLoginWithOTP - ? "Send OTP" - : "Sign In"} + ? "Send OTP" + : "Sign In"} {/* Login With OTP Button */} @@ -254,4 +241,4 @@ const LoginPage = () => { ); }; -export default LoginPage; \ No newline at end of file +export default LoginPage; diff --git a/src/pages/authentication/TenantSelectionPage.jsx b/src/pages/authentication/TenantSelectionPage.jsx index 22b72292..18028902 100644 --- a/src/pages/authentication/TenantSelectionPage.jsx +++ b/src/pages/authentication/TenantSelectionPage.jsx @@ -18,7 +18,7 @@ const TenantSelectionPage = () => { chooseTenant(tenantId); }; - const {mutate:handleLogout,isPending:isLogouting} = useLogout(()=>{}) + const {mutate:handleLogout,isPending:isLogouting} = useLogout() useEffect(() => { diff --git a/src/router/ProtectedRoute.jsx b/src/router/ProtectedRoute.jsx index 68dd847f..d174652c 100644 --- a/src/router/ProtectedRoute.jsx +++ b/src/router/ProtectedRoute.jsx @@ -2,13 +2,69 @@ import React, { useState, useEffect } from "react"; import { Navigate, Outlet } from "react-router-dom"; import { jwtDecode } from "jwt-decode"; import AuthRepository from "../repositories/AuthRepository"; +import { removeSession } from "../utils/authUtils"; + +const isTokenExpired = (token) => { + if (!token) return true; + try { + const { exp } = jwtDecode(token); + return exp * 1000 < Date.now(); + } catch { + return true; + } +}; + +const validateToken = async () => { + const token = + localStorage.getItem("jwtToken") || + sessionStorage.getItem("jwtToken"); + + const refreshTokenStored = + localStorage.getItem("refreshToken") || + sessionStorage.getItem("refreshToken"); + + if (!refreshTokenStored){ + console.log("no refrh tokem"); + removeSession() + return false + }; + + if (isTokenExpired(token)) { + return await attemptTokenRefresh(refreshTokenStored); + } + + return true; +}; + +const attemptTokenRefresh = async (storedRefreshToken) => { + try { + const currentToken = + localStorage.getItem("jwtToken") || + sessionStorage.getItem("jwtToken"); + + const response = await AuthRepository.refreshToken({ + token: currentToken, + refreshToken: storedRefreshToken, + }); + + const { token: newToken, refreshToken: newRefreshToken } = response.data; + + if (localStorage.getItem("jwtToken")) { + localStorage.setItem("jwtToken", newToken); + localStorage.setItem("refreshToken", newRefreshToken); + } else { + sessionStorage.setItem("jwtToken", newToken); + sessionStorage.setItem("refreshToken", newRefreshToken); + } + + return true; + } catch (error) { + console.error("Token refresh failed:", error); + return false; + } +}; const ProtectedRoute = () => { - // const isAuthenticated = localStorage.getItem("jwtToken"); // Example authentication check - // // const isAuthenticated = true; - // isTokenValid(); - // return isAuthenticated ? : - const [isAuthenticated, setIsAuthenticated] = useState(null); useEffect(() => { @@ -21,80 +77,10 @@ const ProtectedRoute = () => { }, []); if (isAuthenticated === null) { - return
Loading...
; // Show a loader while checking + return
Loading...
; } return isAuthenticated ? : ; }; -// Function to check if the token is expired -const isTokenExpired = (token) => { - if (!token) return true; - try { - const { exp } = jwtDecode(token); - return exp * 1000 < Date.now(); // Check if expired - } catch (error) { - return true; // If decoding fails, treat as expired - } -}; - -// Function to validate and refresh the token if expired -export const validateToken = async () => { - const token = localStorage.getItem("jwtToken"); - const refreshTokenStored = localStorage.getItem("refreshToken"); - // If refresh token is absent, cannot proceed - if (!refreshTokenStored) { - console.warn("No refresh token available. Redirecting to login."); - return false; - } - - // If access token expired, try to refresh - if (isTokenExpired(token)) { - return await attemptTokenRefresh(refreshTokenStored); - } - return true; -}; - -// Attempt to refresh the access token -const attemptTokenRefresh = async (storedRefreshToken) => { - try { - const response = await AuthRepository.refreshToken({ - token: localStorage.getItem("jwtToken"), - refreshToken: storedRefreshToken, - }); - - localStorage.setItem("jwtToken", response.data.token); - localStorage.setItem("refreshToken", response.data.refreshToken); - return true; - // api - // .post("/api/auth/refresh-token", { - // token: localStorage.getItem("jwtToken"), - // refreshToken: refreshToken, - // }) - // .then((data) => { - // localStorage.setItem("jwtToken", response.data.token); - // localStorage.setItem("refreshToken", response.data.refreshToken); - // return true; - // }) - // .catch((error) => { - // console.error("Token refresh failed:", error); - // }); - - // const refreshToken = localStorage.getItem("refreshToken"); - // const response = await axiosClient.post(`/api/auth/refresh-token`, { - // token: localStorage.getItem("jwtToken"), - // refreshToken: refreshToken, - // }); - - // if (response.status === 200) { - // localStorage.setItem("jwtToken", response.data.token); - // localStorage.setItem("refreshToken", response.data.refreshToken); - // return true; - // } - } catch (error) { - console.error("Token refresh failed:", error); - return false; - } -}; - export default ProtectedRoute; diff --git a/src/utils/authUtils.js b/src/utils/authUtils.js index e69de29b..8e266583 100644 --- a/src/utils/authUtils.js +++ b/src/utils/authUtils.js @@ -0,0 +1,7 @@ +export const removeSession = () => { + localStorage.removeItem("jwtToken"); + localStorage.removeItem("refreshToken"); + sessionStorage.removeItem("jwtToken"); + sessionStorage.removeItem("refreshToken"); + localStorage.removeItem("ctnt"); +}; \ No newline at end of file diff --git a/src/utils/axiosClient.jsx b/src/utils/axiosClient.jsx index 62e9b3bb..1cc0c7cd 100644 --- a/src/utils/axiosClient.jsx +++ b/src/utils/axiosClient.jsx @@ -16,14 +16,15 @@ export const axiosClient = axios.create({ // Auto retry failed requests (e.g., network issues) axiosRetry(axiosClient, { retries: 3 }); - +debugger // Request Interceptor — Add Bearer token if required axiosClient.interceptors.request.use( async (config) => { const requiresAuth = config.authRequired !== false; // default to true if (requiresAuth) { - const token = localStorage.getItem("jwtToken"); + const token = + localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken"); if (token) { config.headers["Authorization"] = `Bearer ${token}`; config._retry = true; @@ -72,7 +73,7 @@ axiosClient.interceptors.response.use( if (status === 401 && !isRefreshRequest) { originalRequest._retry = true; - const refreshToken = localStorage.getItem("refreshToken"); + const refreshToken = localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken"); if ( !refreshToken || @@ -87,15 +88,20 @@ axiosClient.interceptors.response.use( try { // Refresh token call const res = await axiosClient.post("/api/Auth/refresh-token", { - token: localStorage.getItem("jwtToken"), + token: localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken"), refreshToken, }); const { token, refreshToken: newRefreshToken } = res.data.data; // Save updated tokens - localStorage.setItem("jwtToken", token); - localStorage.setItem("refreshToken", newRefreshToken); + if (localStorage.getItem("jwtToken")) { + localStorage.setItem("jwtToken", token); + localStorage.setItem("refreshToken", newRefreshToken); + } else { + sessionStorage.setItem("jwtToken", token); + sessionStorage.setItem("refreshToken", newRefreshToken); + } startSignalR(); // Set Authorization header