Implement automatic logout after inactivity and improve authentication

Adds session timeout functionality using `useSessionTimeout` hook and updates session maxAge to 15 minutes in server routes.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: dec54137-d5a2-4406-8ad4-5f2f9fb110bf
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/56f97713-69fd-41d1-b933-ff4440458da0.jpg
This commit is contained in:
michaelschal committed 2025-07-10 09:51:34 +00:00
1 parent 0db1bffa94
commit f828278efa
3 files changed
+116 -1

No files matched your search

+4
View File
@@ -3,11 +3,15 @@ import { queryClient } from "./lib/queryClient";
import { Toaster } from "@/components/ui/toaster";
import { TooltipProvider } from "@/components/ui/tooltip";
import { useAuth } from "@/hooks/useAuth";
import { useSessionTimeout } from "@/hooks/useSessionTimeout";
import Login from "@/pages/Login";
import Dashboard from "@/pages/Dashboard";
function AppContent() {
const { isAuthenticated, isLoading } = useAuth();
// Initialize session timeout management
useSessionTimeout();
if (isLoading) {
return (
+102
View File
@@ -0,0 +1,102 @@
import { useEffect, useRef, useCallback } from 'react';
import { useAuth } from './useAuth';
const SESSION_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
const WARNING_TIME_MS = 2 * 60 * 1000; // 2 minutes before timeout
export function useSessionTimeout() {
const { isAuthenticated } = useAuth();
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const warningTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const lastActivityRef = useRef<number>(Date.now());
const logout = useCallback(() => {
// Redirect to logout endpoint which will handle the logout process
window.location.href = '/api/auth/logout';
}, []);
const showWarning = useCallback(() => {
// Show a warning that session will expire soon
const remainingTime = Math.ceil((SESSION_TIMEOUT_MS - (Date.now() - lastActivityRef.current)) / 1000 / 60);
if (confirm(`Votre session va expirer dans ${remainingTime} minute(s). Voulez-vous rester connecté ?`)) {
// User wants to stay logged in, reset the timer
resetTimer();
} else {
// User chose to logout or didn't respond
logout();
}
}, [logout]);
const resetTimer = useCallback(() => {
if (!isAuthenticated) return;
lastActivityRef.current = Date.now();
// Clear existing timeouts
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (warningTimeoutRef.current) {
clearTimeout(warningTimeoutRef.current);
}
// Set warning timeout (2 minutes before expiration)
warningTimeoutRef.current = setTimeout(showWarning, SESSION_TIMEOUT_MS - WARNING_TIME_MS);
// Set logout timeout
timeoutRef.current = setTimeout(logout, SESSION_TIMEOUT_MS);
}, [isAuthenticated, logout, showWarning]);
const handleActivity = useCallback(() => {
resetTimer();
}, [resetTimer]);
useEffect(() => {
if (!isAuthenticated) {
// Clear timeouts if user is not authenticated
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (warningTimeoutRef.current) {
clearTimeout(warningTimeoutRef.current);
}
return;
}
// Start the timer when component mounts or user becomes authenticated
resetTimer();
// Activity events to monitor
const events = [
'mousedown',
'mousemove',
'keypress',
'scroll',
'touchstart',
'click'
];
// Add event listeners for user activity
events.forEach(event => {
document.addEventListener(event, handleActivity, true);
});
// Cleanup function
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (warningTimeoutRef.current) {
clearTimeout(warningTimeoutRef.current);
}
events.forEach(event => {
document.removeEventListener(event, handleActivity, true);
});
};
}, [isAuthenticated, handleActivity, resetTimer]);
return {
resetTimer,
timeRemaining: () => Math.max(0, SESSION_TIMEOUT_MS - (Date.now() - lastActivityRef.current))
};
}
+10 -1
View File
@@ -61,7 +61,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
saveUninitialized: false,
cookie: {
secure: false, // Set to true in production with HTTPS
maxAge: 24 * 60 * 60 * 1000 // 24 hours
maxAge: 15 * 60 * 1000 // 15 minutes
}
}));
@@ -96,6 +96,15 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
app.get('/api/auth/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
console.error('Logout error:', err);
}
res.redirect('/');
});
});
app.post('/api/auth/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {