🧠 Introduction
In modern web applications, security extends far beyond authentication - it’s also about protecting active user sessions from unauthorized access.
One often-overlooked feature in this area is automatic logout due to inactivity.
Imagine a user leaving a laptop unlocked with sensitive data on-screen. Without an inactivity timeout, that session remains vulnerable.
In this post, we’ll walk through how to implement a robust auto-logout mechanism in Angular that detects user inactivity, displays a warning dialog, and safely logs the user out after a configurable timeout period.
🚨 The Problem
Enterprise and data-sensitive applications often need to:
- Automatically terminate sessions after inactivity
- Display a warning before logout
- Detect real…
🧠 Introduction
In modern web applications, security extends far beyond authentication - it’s also about protecting active user sessions from unauthorized access.
One often-overlooked feature in this area is automatic logout due to inactivity.
Imagine a user leaving a laptop unlocked with sensitive data on-screen. Without an inactivity timeout, that session remains vulnerable.
In this post, we’ll walk through how to implement a robust auto-logout mechanism in Angular that detects user inactivity, displays a warning dialog, and safely logs the user out after a configurable timeout period.
🚨 The Problem
Enterprise and data-sensitive applications often need to:
- Automatically terminate sessions after inactivity
- Display a warning before logout
- Detect real user activity (not just page loads)
- Prevent memory leaks via proper cleanup
- Maintain a smooth, non-intrusive user experience
🧩 The Solution: InactivityService
We’ll build an InactivityService that can:
- Detect inactivity using multiple event types
- Display a configurable warning dialog
- Auto-logout after a timeout
- Clean up resources properly
- Work seamlessly with Angular change detection
⚙️ Step 1: Service Setup
import { Injectable, NgZone } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class InactivityService {
private readonly INACTIVITY_TIMEOUT = 60 * 60 * 1000; // 1 hour
private readonly WARNING_TIME = 5 * 60 * 1000; // 5 minutes
private lastActivityTime = Date.now();
private inactivityTimer: any = null;
private warningTimer: any = null;
private isWarningShown = false;
private isTracking = false;
private readonly activityEvents = [
'mousedown', 'mousemove', 'keypress',
'scroll', 'touchstart', 'click'
];
constructor(private ngZone: NgZone) {}
}
Key Design Choices
- Multiple event types: Tracks various forms of user input.
- NgZone usage: Ensures Angular detects UI updates correctly.
- State flags: Manage active state and prevent duplicate warnings.
🖱️ Step 2: Tracking User Activity
startTracking(): void {
if (this.isTracking) return;
this.isTracking = true;
this.resetTimer();
this.addActivityListeners();
}
private addActivityListeners(): void {
this.activityEvents.forEach(event => {
document.addEventListener(event, this.onActivity.bind(this), {
passive: true,
capture: true
});
});
}
private onActivity(): void {
if (!this.isTracking) return;
this.ngZone.run(() => {
this.resetActivity();
});
}
private resetActivity(): void {
this.lastActivityTime = Date.now();
this.isWarningShown = false;
this.resetTimer();
}
Why This Matters
passive: trueimproves scroll performance.- Capturing phase ensures early event detection.
NgZone.run()allows Angular to refresh bindings if needed.
⏰ Step 3: Timer Management
private resetTimer(): void {
this.clearTimers();
// Warning before logout
this.warningTimer = setTimeout(() => {
this.showInactivityWarning();
}, this.INACTIVITY_TIMEOUT - this.WARNING_TIME);
// Auto-logout
this.inactivityTimer = setTimeout(() => {
this.performLogout();
}, this.INACTIVITY_TIMEOUT);
}
private clearTimers(): void {
if (this.inactivityTimer) clearTimeout(this.inactivityTimer);
if (this.warningTimer) clearTimeout(this.warningTimer);
this.inactivityTimer = this.warningTimer = null;
}
Timer Strategy
- Separate timers for warning and logout.
- Reset on any detected activity.
- Clear all on logout to prevent memory leaks.
⚠️ Step 4: Displaying the Warning Dialog
We’ll create a minimal dialog using vanilla JavaScript to avoid external dependencies.
private showInactivityWarning(): void {
if (this.isWarningShown || !this.isTracking) return;
const existing = document.getElementById('inactivity-warning');
if (existing) return;
this.isWarningShown = true;
const overlay = document.createElement('div');
overlay.id = 'inactivity-warning';
Object.assign(overlay.style, {
position: 'fixed',
top: '0', left: '0', right: '0', bottom: '0',
background: 'rgba(0,0,0,0.4)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: '9999'
});
const dialog = document.createElement('div');
Object.assign(dialog.style, {
background: '#fff',
padding: '24px',
borderRadius: '8px',
textAlign: 'center',
boxShadow: '0 4px 10px rgba(0,0,0,0.3)',
width: '300px'
});
const title = document.createElement('h3');
title.textContent = 'Session Timeout Warning';
const message = document.createElement('p');
message.textContent = 'You will be logged out in 5 minutes due to inactivity.';
const stayBtn = document.createElement('button');
stayBtn.textContent = 'Stay Logged In';
stayBtn.onclick = () => {
this.dismissWarning();
this.resetActivity();
};
const logoutBtn = document.createElement('button');
logoutBtn.textContent = 'Logout Now';
logoutBtn.onclick = () => {
this.dismissWarning();
this.performLogout();
};
dialog.append(title, message, stayBtn, logoutBtn);
overlay.appendChild(dialog);
document.body.appendChild(overlay);
}
private dismissWarning(): void {
const warning = document.getElementById('inactivity-warning');
if (warning) warning.remove();
this.isWarningShown = false;
}
Advantages of This Approach
- Zero external dependencies
- Works even if Angular rendering pauses
- Customizable look and feel
🚪 Step 5: Performing Logout
private performLogout(): void {
if (!this.isTracking) return;
this.stopTracking();
this.dismissWarning();
// Clear any cached data
localStorage.clear();
sessionStorage.clear();
// Redirect to login or home page
window.location.href = '/login';
}
stopTracking(): void {
if (!this.isTracking) return;
this.isTracking = false;
this.clearTimers();
this.activityEvents.forEach(event => {
document.removeEventListener(event, this.onActivity.bind(this));
});
}
🔗 Integration in Your Angular App
To enable inactivity tracking globally, start the service in your main AppComponent or after user login:
export class AppComponent implements OnInit {
constructor(private inactivityService: InactivityService) {}
ngOnInit(): void {
this.inactivityService.startTracking();
console.log('Inactivity tracking enabled');
}
}
🧭 Best Practices
1. Configurable Timeouts
Use environment variables or role-based settings:
private readonly INACTIVITY_TIMEOUT = 15 * 60 * 1000; // 15 minutes for admins
2. Server-Side Session Expiry
Don’t rely solely on client logic-implement:
- Short-lived JWTs
- Refresh token rotation
- Server-side session validation
3. UX & Accessibility
- Provide clear messages
- Allow time to respond
- Add ARIA attributes for screen readers
4. Performance
- Use
passive: truelisteners - Debounce high-frequency events if needed
- Clean up listeners on component destroy
🧪 Testing Tips
- Simulate user inactivity with fake timers
- Verify that warning appears before logout
- Ensure timers reset on user activity
Example test snippet:
it('should reset activity on user interaction', () => {
service.startTracking();
document.dispatchEvent(new MouseEvent('mousemove'));
expect(service['lastActivityTime']).toBeGreaterThan(Date.now() - 1000);
});
💡 Real-World Benefits
Implementing inactivity logout helps you achieve:
- 🔒 Improved session security
- ✅ Compliance with standards like HIPAA / GDPR
- 💬 User trust through clear timeout warnings
- 🧹 Cleaner memory management via proper event cleanup
🏁 Conclusion
Building an auto-logout system may seem small, but it significantly boosts your application’s security posture.
This approach offers:
- Comprehensive user activity detection
- Configurable timeout and warning
- Lightweight implementation with no dependencies
- Full Angular compatibility
Secure, user-friendly, and easy to integrate - this solution protects both your users and your platform.
📚 Resources
💬 Discussion
What session timeout strategies do you use in your applications?
Let’s exchange ideas below 👇