In today's web applications, staying logged in on a website for an extended period, whether an hour or two, can pose security risks. During this time, background API calls might still be running, and if you're not actively using the site, someone else could potentially access it with your credentials. To mitigate these risks and reduce unnecessary server requests, it's crucial to monitor user activity, such as cursor movement or key presses. If the system detects inactivity for a specified duration, it can automatically log the user out. This feature, known as Idle Timeout, is essential for modern web applications.
Letโs dive into implementing idle timeout in Angular.
idleService.ts
import { Injectable } from '@angular/core';
import { interval, Observable, Subject, Subscription, throttle } from 'rxjs';
export class IdleService {
private idleSubject = new Subject<boolean>();
private timeout = 1200; //seconds
private lastActivity?: Date;
private idleCheckInterval = 600; //seconds
private idleSubscription?: Subscription
constructor() {
this.resetTimer();
this.startWatching();
}
get idleState(): Observable<boolean> {
return this.idleSubject.asObservable();
}
private startWatching(): void {
this.idleSubscription = interval(this.idleCheckInterval * 1000)
.pipe(throttle(() => interval(1000)))
.subscribe(() => {
const now = new Date();
if (now.getTime() - this.lastActivity?.getTime()! > this.timeout * 1000) {
this.idleSubject.next(true);
}
});
}
resetTimer(): void {
this.lastActivity = new Date();
this.idleSubject.next(false);
}
stopWatching(): void {
if (this.idleSubscription) {
this.idleSubscription.unsubscribe();
}
}
}
This service monitors user activity and checks if the user has been idle for a specified duration. If so, it triggers an event through idleSubject
app.component.ts
import { inject, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { IdleService } from 'path-to-your-idleService';
export class AppComponent implements OnInit, OnDestroy {
private idleSubscription?: Subscription;
private idleService = inject(IdleService);
ngOnInit(): void {
this.idleSubscription = this.idleService.idleState.subscribe((isIdle) => {
if (isIdle) {
console.log("user is idle");
// Add logic for handling idle state, e.g., logging out
} else {
console.log("user is active");
}
});
}
onUserAction(): void {
this.idleService.resetTimer();
}
ngOnDestroy(): void {
if (this.idleSubscription) {
this.idleSubscription.unsubscribe();
}
}
Subscribes to the idle state and handles the user being idle or active. It also resets the idle timer on user interactions such as mouse movements, clicks, or key presses.
app.component.html
<div
(mousemove)="onUserAction()"
(click)="onUserAction()"
(keypress)="onUserAction()"
>
<router-outlet />
</div>
Captures user interactions to reset the idle timer whenever activity is detected.
Top comments (1)
In times when applications communicate with REST servers using tokens, JWTs, or others, these functions seem unnecessary.
Of course, if the server issues long-lived tokens, that is a flaw on the server's part.
Instead, I believe a good control mechanism should be implemented for the refresh token. Before reaching out to the server to refresh the access token, we could calculate the expiration of the refresh token and notify users that it has expired, or we could monitor the refresh token's expiration time and alert users a few minutes before it is about to expire, indicating that an action needs to be taken or the session will end.
Itโs also important to consider that often the application in the browser is performing tasks and does not require communication with the REST server, which means the refresh token could expire, leading to a poor user experience by notifying them that their session is about to expire. In such cases, it would be essential to refresh the token without interrupting the user's task.