We wanted an activity monitor to force logging off authenticated users after a certain period of inactivity. So naturally we searched the web to see how others implemented similar solutions. The prominent AI summary suggested the following, which we initially implemented.
<script>
let inactivityTimeout;
// Function to log the user out
function logOut() {
alert("You have been logged out due to inactivity.");
// Redirect to a logout page or perform logout logic
window.location.href = "/logout";
}
// Reset the inactivity timer
function resetTimer() {
clearTimeout(inactivityTimeout);
inactivityTimeout = setTimeout(logOut, 5 * 60 * 1000); // 5 minutes
}
// Add event listeners for user activity
function setupInactivityTimer() {
document.addEventListener("mousemove", resetTimer);
document.addEventListener("keydown", resetTimer);
document.addEventListener("click", resetTimer);
document.addEventListener("scroll", resetTimer);
resetTimer(); // Start the timer initially
}
// Initialize the inactivity timer when the page loads
window.onload = setupInactivityTimer;
</script>
While that gets the job done, the constant recycling of inactivityTimeout struck us as grossly inefficient. Considering how many times those events are triggered while interacting with pages, it was determined a poor solution.
We instead decided to use a Date variable, updated as each event is triggered, and monitored in reasonable intervals. This example below shows how we check that variable once a minute, determine if more than 10 minutes have passed since last activity, and automatically log out.
<script>
let userLastActive
const setUserLastActive = () => {
userLastActive = Date.now()
}
/**
* Monitoring 'wheel' (standard) and 'mousewheel' (deprecated) due to mixed support
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event#browser_compatibility
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/mousewheel_event#browser_compatibility
**/
['mousemove', 'keydown', 'click', 'scroll', 'touchstart', 'wheel', 'mousewheel'].forEach(event => {
document.addEventListener(event, setUserLastActive)
})
setInterval(() => {
if (Date.now() - userLastActive > 10 * 60 * 1000) {
window.location.href = '/logout?inactive'
}
}, 60 * 1000)
</script>
We believe this is much more performant and has no chance of potential (albeit unlikely) overflows. While AI can be useful for inspiration, we rarely find the code it provides to be very efficient.
Notes
- AI-provided example uses JavaScript
alert()to notify users before logout.- Alerts require user interaction, leaving potentially sensitive visible. During which time the page can be inspected, modified, or have actions interrupted by browser console tampering.
- Better solution is displaying a warning (added below) prior to automatic logout, then proceed if the user fails to respond.
- AI-provided example excludes important considerations for interaction-specific events such as
touchstart,wheel, andmousewheel.
Opinion
All AI does, is what we did before it became a buzzword… search for solutions, find prominent results, and decide if they look like good ideas. Which we must still do, because AI simply regurgitates code, using those same underlying search results. 😒
AI (artificial intelligence) search results are ASS (automated search slop). 🤣
Bonus
Show Bootstrap Toast notification before automatically logging out…
// Every minute
setInterval(() => {
const now = Date.now()
// Log out after 10 minutes
if (now - userLastActive > 10 * 60 * 1000) {
return window.location.href = 'users/logout?inactive'
}
// Warn after 8 minutes
if (now - userLastActive > 8 * 60 * 1000) {
bootstrap.Toast.getOrCreateInstance(document.getElementById('toast')).show()
}
}, 60 * 1000)
Toast 🍞
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
<div class="toast-header">
<strong class="me-auto">Inactivity Warning</strong>
<button type="button" class="btn-close ms-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
You will be automatically logged out in 2 minutes.
</div>
</div>
</div>
Interacting with the Toast or page updates userLastActive so nothing else is necessary. 🍻