DEV Community

SANKET PATIL
SANKET PATIL

Posted on

Building an Auto-Logout System for Angular Applications: Securing User Sessions with Inactivity Detection

🧠 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) {}
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

Why This Matters

  • passive: true improves 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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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));
  });
}
Enter fullscreen mode Exit fullscreen mode

🔗 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');
  }
}
Enter fullscreen mode Exit fullscreen mode

🧭 Best Practices

1. Configurable Timeouts

Use environment variables or role-based settings:

private readonly INACTIVITY_TIMEOUT = 15 * 60 * 1000; // 15 minutes for admins
Enter fullscreen mode Exit fullscreen mode

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: true listeners
  • 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);
});
Enter fullscreen mode Exit fullscreen mode

💡 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 👇

Top comments (0)