A session that expires silently while a user is mid-transaction forces an abrupt authentication failure — potentially losing form data, a partially entered transfer, or an in-flight payment. Without a warning, users either encounter jarring redirects or, worse, begin abandoning financial tasks they cannot complete. CWE-613 and PCI-DSS Req 8.2.8 require that session expiration be communicated to users. Beyond compliance, an unwarned expiry increases the probability that users disable session timeouts through workarounds, defeating the security control entirely. A 2-minute warning with an 'extend' option preserves both security and usability.
Medium because silent expiry during active financial tasks creates UX failures that drive users to circumvent session security controls, indirectly undermining the inactivity timeout that PCI-DSS Req 8.2.8 mandates.
Add a warning modal component in src/components/SessionWarning.tsx that fires at 120 seconds before the session expiration boundary:
// lib/sessionWarning.ts
export function useSessionWarning(sessionTimeoutSeconds: number) {
const [showWarning, setShowWarning] = useState(false);
useEffect(() => {
const warningMs = (sessionTimeoutSeconds - 120) * 1000; // 2 min before expiry
const timer = setTimeout(() => setShowWarning(true), warningMs);
return () => clearTimeout(timer);
}, [sessionTimeoutSeconds]);
const handleExtend = async () => {
await fetch('/api/auth/extend-session', { method: 'POST' });
setShowWarning(false);
};
return { showWarning, handleExtend };
}
The modal must present at least two choices: extend (calls the server) and logout. A countdown timer inside the modal is strongly recommended so the user knows how many seconds remain.
finserv-session-security.session-lifecycle.timeout-warningmedium"0 timeout warning components found — session expires without user notification" or "Warning at 30 seconds — below 120 second minimum"src/components/SessionWarning.tsx):
// lib/sessionWarning.ts
export function useSessionWarning(sessionTimeout: number) {
const [showWarning, setShowWarning] = useState(false);
useEffect(() => {
const warningTime = (sessionTimeout - 2 * 60) * 1000; // 2 min before expiry
const timer = setTimeout(() => setShowWarning(true), warningTime);
return () => clearTimeout(timer);
}, [sessionTimeout]);
const handleExtend = async () => {
await fetch('/api/auth/extend-session', { method: 'POST' });
setShowWarning(false);
};
return { showWarning, handleExtend };
}