addded remember me functionality

This commit is contained in:
pramod.mahajan 2025-09-29 00:13:52 +05:30
parent 198e31290c
commit 61b209a082
6 changed files with 137 additions and 147 deletions

View File

@ -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()
},
});
}
};

View File

@ -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 (
<div className="col-12 col-lg-5 col-xl-4 d-flex align-items-center p-4 p-sm-5 bg-gray-60">
<div className="w-100" style={{ maxWidth: 420, margin: "0 auto" }}>
@ -106,36 +121,6 @@ const LoginPage = () => {
{/* Password */}
{!IsLoginWithOTP && (
<>
{/* <div className="mb-3 text-start">
<label htmlFor="password" className="form-label">
Password
</label>
<div className="input-group input-group-merge">
<input
type={hidepass ? "password" : "text"}
id="password"
className={`form-control ${errors.password ? "is-invalid" : ""
}`}
placeholder="••••••••"
{...register("password")}
/>
<span
className="input-group-text cursor-pointer"
onClick={() => setHidepass(!hidepass)}
>
<i className={`bx ${hidepass ? "bx-hide" : "bx-show"}`}></i>
</span>
</div>
{errors.password && (
<div
className="invalid-feedback text-start"
style={{ fontSize: "12px" }}
>
{errors.password.message}
</div>
)}
</div> */}
<div className="mb-3 form-password-toggle text-start">
<label htmlFor="password" className="form-label">
Password
@ -146,8 +131,9 @@ const LoginPage = () => {
type={hidepass ? "password" : "text"}
autoComplete="new-password"
id="password"
className={`form-control form-control-xl shadow-none ${errors.password ? "is-invalid" : ""
}`}
className={`form-control form-control-xl shadow-none ${
errors.password ? "is-invalid" : ""
}`}
name="password"
{...register("password")}
placeholder="••••••••••••"
@ -168,15 +154,16 @@ const LoginPage = () => {
</span>
</div>
{/* ✅ Error message */}
{errors.password && (
<div className="invalid-feedback text-start" style={{ fontSize: "12px" }}>
<div
className="invalid-feedback text-start"
style={{ fontSize: "12px" }}
>
{errors.password.message}
</div>
)}
</div>
{/* Remember Me + Forgot Password */}
<div className="mb-3 d-flex justify-content-between align-items-center">
<div className="form-check">
@ -209,8 +196,8 @@ const LoginPage = () => {
{loading
? "Please Wait..."
: IsLoginWithOTP
? "Send OTP"
: "Sign In"}
? "Send OTP"
: "Sign In"}
</button>
{/* Login With OTP Button */}
@ -254,4 +241,4 @@ const LoginPage = () => {
);
};
export default LoginPage;
export default LoginPage;

View File

@ -18,7 +18,7 @@ const TenantSelectionPage = () => {
chooseTenant(tenantId);
};
const {mutate:handleLogout,isPending:isLogouting} = useLogout(()=>{})
const {mutate:handleLogout,isPending:isLogouting} = useLogout()
useEffect(() => {

View File

@ -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 ? <Outlet /> : <Navigate to="/auth/login" />
const [isAuthenticated, setIsAuthenticated] = useState(null);
useEffect(() => {
@ -21,80 +77,10 @@ const ProtectedRoute = () => {
}, []);
if (isAuthenticated === null) {
return <div>Loading...</div>; // Show a loader while checking
return <div>Loading...</div>;
}
return isAuthenticated ? <Outlet /> : <Navigate to="/auth/login" replace />;
};
// 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;

View File

@ -0,0 +1,7 @@
export const removeSession = () => {
localStorage.removeItem("jwtToken");
localStorage.removeItem("refreshToken");
sessionStorage.removeItem("jwtToken");
sessionStorage.removeItem("refreshToken");
localStorage.removeItem("ctnt");
};

View File

@ -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