Refactor Dashboard component to handle authentication errors and improve token validation logic
This commit is contained in:
@@ -84,7 +84,10 @@ export const login = async (username, password) => {
|
|||||||
|
|
||||||
export const refreshToken = async () => {
|
export const refreshToken = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await client.post(API_ENDPOINTS.AUTH.REFRESH);
|
const fingerprint = localStorage.getItem('fingerprint');
|
||||||
|
const response = await client.post(API_ENDPOINTS.AUTH.REFRESH, {
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
});
|
||||||
if (response.data.access_token && response.data.token_type === 'bearer') {
|
if (response.data.access_token && response.data.token_type === 'bearer') {
|
||||||
localStorage.setItem('access_token', response.data.access_token);
|
localStorage.setItem('access_token', response.data.access_token);
|
||||||
}
|
}
|
||||||
@@ -92,7 +95,6 @@ export const refreshToken = async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
localStorage.removeItem('access_token');
|
localStorage.removeItem('access_token');
|
||||||
localStorage.removeItem('fingerprint');
|
localStorage.removeItem('fingerprint');
|
||||||
cookies.remove('refresh_token');
|
|
||||||
throw error.response?.data || error.message;
|
throw error.response?.data || error.message;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -7,22 +7,38 @@ export function cn(...inputs) {
|
|||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let refreshPromise = null;
|
||||||
|
|
||||||
export async function jwtexp(token) {
|
export async function jwtexp(token) {
|
||||||
|
console.log("jwtexp called from:", new Error().stack);
|
||||||
const decoded = decode(token);
|
const decoded = decode(token);
|
||||||
const currentTime = Date.now() / 1000;
|
const currentTime = Date.now() / 1000;
|
||||||
const tokenExp = decoded.exp;
|
const tokenExp = decoded.exp;
|
||||||
|
console.log("Token exp:", tokenExp, "Current time:", currentTime);
|
||||||
|
|
||||||
if (tokenExp < currentTime || tokenExp - currentTime < 120) {
|
if (tokenExp < currentTime || tokenExp - currentTime < 120) {
|
||||||
console.log("Token needs refresh");
|
console.log("Token needs refresh");
|
||||||
|
|
||||||
|
if (refreshPromise) {
|
||||||
|
console.log("Refresh already in progress, waiting...");
|
||||||
|
return await refreshPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log("Starting token refresh...");
|
||||||
await refreshToken();
|
await refreshToken();
|
||||||
console.log("Token refreshed successfully");
|
console.log("Token refreshed successfully");
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to refresh token:", error);
|
console.error("Failed to refresh token:", error);
|
||||||
return false;
|
return false;
|
||||||
|
} finally {
|
||||||
|
refreshPromise = null;
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return await refreshPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Token is valid");
|
console.log("Token is valid");
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import App from './App.jsx'
|
|||||||
import { BrowserRouter } from 'react-router'
|
import { BrowserRouter } from 'react-router'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
<StrictMode>
|
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<App />
|
<App />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</StrictMode>,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,11 +18,32 @@ const menuItems = [
|
|||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const [tasksFromBackend, setTasksFromBackend] = useState([]);
|
const [tasksFromBackend, setTasksFromBackend] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [authError, setAuthError] = useState(false);
|
||||||
|
|
||||||
// Fetch tasks
|
// Fetch tasks
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('Dashboard useEffect triggered');
|
||||||
const fetchTasks = async () => {
|
const fetchTasks = async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log('Starting fetchTasks...');
|
||||||
|
const token = localStorage.getItem('access_token');
|
||||||
|
if (token) {
|
||||||
|
console.log('Token found, validating...');
|
||||||
|
const isTokenValid = await jwtexp(token);
|
||||||
|
if (!isTokenValid) {
|
||||||
|
console.error('Token validation failed');
|
||||||
|
setAuthError(true);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('No access token found');
|
||||||
|
setAuthError(true);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Fetching user tasks...');
|
||||||
const response = await getUserTasks(1);
|
const response = await getUserTasks(1);
|
||||||
console.log('Tasks from backend:', response);
|
console.log('Tasks from backend:', response);
|
||||||
setTasksFromBackend(response);
|
setTasksFromBackend(response);
|
||||||
@@ -98,6 +119,25 @@ export default function Dashboard() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (authError) {
|
||||||
|
return (
|
||||||
|
<div className="dashboard-container">
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-xl text-red-500 mb-4">Authentication Error</p>
|
||||||
|
<p className="text-gray-600">Please log in again</p>
|
||||||
|
<button
|
||||||
|
onClick={() => window.location.href = '/login'}
|
||||||
|
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
Go to Login
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-container">
|
<div className="dashboard-container">
|
||||||
{/* Navigation Rail - Material Design 3 */}
|
{/* Navigation Rail - Material Design 3 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user