addded remember me functionality
This commit is contained in:
parent
198e31290c
commit
61b209a082
@ -12,6 +12,7 @@ import {
|
|||||||
closeAuthModal,
|
closeAuthModal,
|
||||||
openAuthModal,
|
openAuthModal,
|
||||||
} from "../slices/localVariablesSlice.jsx";
|
} from "../slices/localVariablesSlice.jsx";
|
||||||
|
import { removeSession } from "../utils/authUtils.js";
|
||||||
|
|
||||||
export const useTenants = () => {
|
export const useTenants = () => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@ -28,18 +29,24 @@ export const useSelectTenant = (onSuccessCallBack) => {
|
|||||||
const res = await AuthRepository.selectTenant(tenantId);
|
const res = await AuthRepository.selectTenant(tenantId);
|
||||||
return res.data;
|
return res.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
localStorage.setItem("jwtToken", data.token);
|
if (localStorage.getItem("jwtToken")) {
|
||||||
localStorage.setItem("refreshToken", data.refreshToken);
|
localStorage.setItem("jwtToken", data.token);
|
||||||
|
localStorage.setItem("refreshToken", data.refreshToken);
|
||||||
|
} else {
|
||||||
|
sessionStorage.setItem("jwtToken", data.token);
|
||||||
|
sessionStorage.setItem("refreshToken", data.refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
if (onSuccessCallBack) onSuccessCallBack();
|
if (onSuccessCallBack) onSuccessCallBack();
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
showToast(error.message || "Error while creating project", "error");
|
showToast(error.message || "Error while creating project", "error");
|
||||||
localStorage.removeItem("jwtToken");
|
localStorage.removeItem("jwtToken");
|
||||||
localStorage.removeItem("refreshToken")
|
localStorage.removeItem("refreshToken");
|
||||||
localStorage.removeItem("ctnt")
|
localStorage.removeItem("ctnt");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -55,29 +62,26 @@ export const useAuthModal = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useLogout = ()=>{
|
|
||||||
|
export const useLogout = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
let payload = {refreshToken: localStorage.getItem("refreshToken")}
|
let payload = { refreshToken: localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken") };
|
||||||
return await AuthRepository.logout(payload);
|
return await AuthRepository.logout(payload);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
localStorage.removeItem("jwtToken");
|
removeSession()
|
||||||
localStorage.removeItem("refreshToken");
|
|
||||||
localStorage.removeItem("ctnt");
|
window.location.href = "/auth/login";
|
||||||
localStorage.clear();
|
|
||||||
window.location.href = "/auth/login";
|
|
||||||
if (onSuccessCallBack) onSuccessCallBack();
|
if (onSuccessCallBack) onSuccessCallBack();
|
||||||
},
|
},
|
||||||
|
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
showToast(error.message || "Error while creating project", "error");
|
showToast(error.message || "Error while creating project", "error");
|
||||||
localStorage.removeItem("jwtToken");
|
removeSession()
|
||||||
localStorage.removeItem("refreshToken")
|
|
||||||
localStorage.removeItem("ctnt")
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
@ -16,13 +16,13 @@ const LoginPage = () => {
|
|||||||
|
|
||||||
const loginSchema = IsLoginWithOTP
|
const loginSchema = IsLoginWithOTP
|
||||||
? z.object({
|
? z.object({
|
||||||
username: z.string().trim().email({ message: "Valid email required" }),
|
username: z.string().trim().email({ message: "Valid email required" }),
|
||||||
})
|
})
|
||||||
: z.object({
|
: z.object({
|
||||||
username: z.string().trim().email({ message: "Valid email required" }),
|
username: z.string().trim().email({ message: "Valid email required" }),
|
||||||
password: z.string().trim().min(1, { message: "Password required" }),
|
password: z.string().trim().min(1, { message: "Password required" }),
|
||||||
rememberMe: z.boolean(),
|
rememberMe: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -41,8 +41,13 @@ const LoginPage = () => {
|
|||||||
password: data.password,
|
password: data.password,
|
||||||
};
|
};
|
||||||
const response = await AuthRepository.login(userCredential);
|
const response = await AuthRepository.login(userCredential);
|
||||||
localStorage.setItem("jwtToken", response.data.token);
|
if (data.rememberMe) {
|
||||||
localStorage.setItem("refreshToken", response.data.refreshToken);
|
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);
|
setLoading(false);
|
||||||
navigate("/auth/switch/org");
|
navigate("/auth/switch/org");
|
||||||
} else {
|
} else {
|
||||||
@ -69,6 +74,16 @@ const LoginPage = () => {
|
|||||||
}
|
}
|
||||||
}, [IsLoginWithOTP]);
|
}, [IsLoginWithOTP]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const token =
|
||||||
|
localStorage.getItem("jwtToken") ||
|
||||||
|
sessionStorage.getItem("jwtToken");
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
navigate("/dashboard", { replace: true });
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
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="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" }}>
|
<div className="w-100" style={{ maxWidth: 420, margin: "0 auto" }}>
|
||||||
@ -106,36 +121,6 @@ const LoginPage = () => {
|
|||||||
{/* Password */}
|
{/* Password */}
|
||||||
{!IsLoginWithOTP && (
|
{!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">
|
<div className="mb-3 form-password-toggle text-start">
|
||||||
<label htmlFor="password" className="form-label">
|
<label htmlFor="password" className="form-label">
|
||||||
Password
|
Password
|
||||||
@ -146,8 +131,9 @@ const LoginPage = () => {
|
|||||||
type={hidepass ? "password" : "text"}
|
type={hidepass ? "password" : "text"}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
id="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"
|
name="password"
|
||||||
{...register("password")}
|
{...register("password")}
|
||||||
placeholder="••••••••••••"
|
placeholder="••••••••••••"
|
||||||
@ -168,15 +154,16 @@ const LoginPage = () => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ✅ Error message */}
|
|
||||||
{errors.password && (
|
{errors.password && (
|
||||||
<div className="invalid-feedback text-start" style={{ fontSize: "12px" }}>
|
<div
|
||||||
|
className="invalid-feedback text-start"
|
||||||
|
style={{ fontSize: "12px" }}
|
||||||
|
>
|
||||||
{errors.password.message}
|
{errors.password.message}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* Remember Me + Forgot Password */}
|
{/* Remember Me + Forgot Password */}
|
||||||
<div className="mb-3 d-flex justify-content-between align-items-center">
|
<div className="mb-3 d-flex justify-content-between align-items-center">
|
||||||
<div className="form-check">
|
<div className="form-check">
|
||||||
@ -209,8 +196,8 @@ const LoginPage = () => {
|
|||||||
{loading
|
{loading
|
||||||
? "Please Wait..."
|
? "Please Wait..."
|
||||||
: IsLoginWithOTP
|
: IsLoginWithOTP
|
||||||
? "Send OTP"
|
? "Send OTP"
|
||||||
: "Sign In"}
|
: "Sign In"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Login With OTP Button */}
|
{/* Login With OTP Button */}
|
||||||
@ -254,4 +241,4 @@ const LoginPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
@ -18,7 +18,7 @@ const TenantSelectionPage = () => {
|
|||||||
chooseTenant(tenantId);
|
chooseTenant(tenantId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {mutate:handleLogout,isPending:isLogouting} = useLogout(()=>{})
|
const {mutate:handleLogout,isPending:isLogouting} = useLogout()
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -2,13 +2,69 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { Navigate, Outlet } from "react-router-dom";
|
import { Navigate, Outlet } from "react-router-dom";
|
||||||
import { jwtDecode } from "jwt-decode";
|
import { jwtDecode } from "jwt-decode";
|
||||||
import AuthRepository from "../repositories/AuthRepository";
|
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 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);
|
const [isAuthenticated, setIsAuthenticated] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -21,80 +77,10 @@ const ProtectedRoute = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (isAuthenticated === null) {
|
if (isAuthenticated === null) {
|
||||||
return <div>Loading...</div>; // Show a loader while checking
|
return <div>Loading...</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isAuthenticated ? <Outlet /> : <Navigate to="/auth/login" replace />;
|
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;
|
export default ProtectedRoute;
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
export const removeSession = () => {
|
||||||
|
localStorage.removeItem("jwtToken");
|
||||||
|
localStorage.removeItem("refreshToken");
|
||||||
|
sessionStorage.removeItem("jwtToken");
|
||||||
|
sessionStorage.removeItem("refreshToken");
|
||||||
|
localStorage.removeItem("ctnt");
|
||||||
|
};
|
@ -16,14 +16,15 @@ export const axiosClient = axios.create({
|
|||||||
|
|
||||||
// Auto retry failed requests (e.g., network issues)
|
// Auto retry failed requests (e.g., network issues)
|
||||||
axiosRetry(axiosClient, { retries: 3 });
|
axiosRetry(axiosClient, { retries: 3 });
|
||||||
|
debugger
|
||||||
// Request Interceptor — Add Bearer token if required
|
// Request Interceptor — Add Bearer token if required
|
||||||
axiosClient.interceptors.request.use(
|
axiosClient.interceptors.request.use(
|
||||||
async (config) => {
|
async (config) => {
|
||||||
const requiresAuth = config.authRequired !== false; // default to true
|
const requiresAuth = config.authRequired !== false; // default to true
|
||||||
|
|
||||||
if (requiresAuth) {
|
if (requiresAuth) {
|
||||||
const token = localStorage.getItem("jwtToken");
|
const token =
|
||||||
|
localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken");
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers["Authorization"] = `Bearer ${token}`;
|
config.headers["Authorization"] = `Bearer ${token}`;
|
||||||
config._retry = true;
|
config._retry = true;
|
||||||
@ -72,7 +73,7 @@ axiosClient.interceptors.response.use(
|
|||||||
if (status === 401 && !isRefreshRequest) {
|
if (status === 401 && !isRefreshRequest) {
|
||||||
originalRequest._retry = true;
|
originalRequest._retry = true;
|
||||||
|
|
||||||
const refreshToken = localStorage.getItem("refreshToken");
|
const refreshToken = localStorage.getItem("refreshToken") || sessionStorage.getItem("refreshToken");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!refreshToken ||
|
!refreshToken ||
|
||||||
@ -87,15 +88,20 @@ axiosClient.interceptors.response.use(
|
|||||||
try {
|
try {
|
||||||
// Refresh token call
|
// Refresh token call
|
||||||
const res = await axiosClient.post("/api/Auth/refresh-token", {
|
const res = await axiosClient.post("/api/Auth/refresh-token", {
|
||||||
token: localStorage.getItem("jwtToken"),
|
token: localStorage.getItem("jwtToken") || sessionStorage.getItem("jwtToken"),
|
||||||
refreshToken,
|
refreshToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { token, refreshToken: newRefreshToken } = res.data.data;
|
const { token, refreshToken: newRefreshToken } = res.data.data;
|
||||||
|
|
||||||
// Save updated tokens
|
// Save updated tokens
|
||||||
localStorage.setItem("jwtToken", token);
|
if (localStorage.getItem("jwtToken")) {
|
||||||
localStorage.setItem("refreshToken", newRefreshToken);
|
localStorage.setItem("jwtToken", token);
|
||||||
|
localStorage.setItem("refreshToken", newRefreshToken);
|
||||||
|
} else {
|
||||||
|
sessionStorage.setItem("jwtToken", token);
|
||||||
|
sessionStorage.setItem("refreshToken", newRefreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
startSignalR();
|
startSignalR();
|
||||||
// Set Authorization header
|
// Set Authorization header
|
||||||
|
Loading…
x
Reference in New Issue
Block a user