feat: auto-logout after 1 hour of inactivity

Tracks user activity events (mouse, keyboard, scroll, touch) and resets
a 60-minute idle timer on each event. Shows a warning toast at 55 minutes,
then calls logoutMutation at 60 minutes to clear the session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-02 23:56:21 -04:00
parent 57c18b404f
commit bcf4111cdf

View File

@@ -1,4 +1,4 @@
import { createContext, ReactNode, useContext } from "react"; import { createContext, ReactNode, useContext, useEffect, useRef } from "react";
import { import {
useQuery, useQuery,
useMutation, useMutation,
@@ -131,6 +131,51 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}, },
}); });
const IDLE_TIMEOUT = 60 * 60 * 1000;
const WARN_BEFORE = 5 * 60 * 1000;
const logoutTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const warnTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const warnedRef = useRef(false);
useEffect(() => {
if (!user) return;
const clearTimers = () => {
if (logoutTimer.current) clearTimeout(logoutTimer.current);
if (warnTimer.current) clearTimeout(warnTimer.current);
};
const resetTimers = () => {
clearTimers();
warnedRef.current = false;
warnTimer.current = setTimeout(() => {
if (!warnedRef.current) {
warnedRef.current = true;
toast({
title: "Session expiring soon",
description: "You will be logged out in 5 minutes due to inactivity.",
duration: 10000,
});
}
}, IDLE_TIMEOUT - WARN_BEFORE);
logoutTimer.current = setTimeout(() => {
logoutMutation.mutate();
}, IDLE_TIMEOUT);
};
const events = ["mousemove", "mousedown", "keydown", "scroll", "touchstart", "click"];
events.forEach((e) => window.addEventListener(e, resetTimers, { passive: true }));
resetTimers();
return () => {
clearTimers();
events.forEach((e) => window.removeEventListener(e, resetTimers));
};
}, [user]);
return ( return (
<AuthContext.Provider <AuthContext.Provider
value={{ value={{