Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Session timeout warning popup in Angular

I'm working on an application that has a session timeout after 30 mins of inactivity. I have a new requirement to pop up a message asking users if they'd like to keep their session active, a couple mins before they're automatically logged out.

Right now the session is managed in what I think is a pretty unorthodox manner, and I need to try to work with what's already there. There's a service used by the App Module called context.service (injected as a provider) that uses a setTimeout to determine when 30 mins of inactivity has expired.

Given that I need access to that countdown, I wanted to create a mirrored timeout that executes 2 mins earlier and fires the modal, asking the user if they want to keep their session open. After injecting NgbModal into the ContextService I receive a circular reference error which seems quite reasonable. It seems a little crazy to try to populate a modal on the DOM using a provider, but I'm not sure what a viable alternative is.

Here's the current state as it exists (with the circular reference error):

// ...
import { SessionExpirationWarning } from '../components/session-expiration-warning/session-expiration-warning.component';
// ....

constructor(
    private _http: HttpClient,
    private _injector: Injector,
    private modalSvc: NgbModal
) {
    // ...
}

// ...

setSessionTimeout() {
    if (this.appConfig === null) { return; }

    clearTimeout(this._timeoutId);
    clearTimeout(this.timeoutWarning);

    const sessionTimeOutConfig = this.appConfig.SessionTimeoutMinutes;

    const SessionTimeoutMinutes = sessionTimeOutConfig === undefined ? 5 : sessionTimeOutConfig;
    const sessionWarningMinutes = 2;

    this._timeoutId = setTimeout(() => {
        this.sessionExpired();
    }, SessionTimeoutMinutes * (60 * 1000));

    this.timeoutWarning = setTimeout(() => {
        if (!this.warningIsActive) {
            const timeOutWarningModal = this.modalSvc.open(SessionExpirationWarning);

            timeOutWarningModal.result.then((modalResponse) => {
                if (modalResponse === true) {
                    this.keepAlive(null);
                }
            });
        }
    }, sessionWarningMinutes * (60 * 1000));
}

The this.timeoutWarning was my attempt at hacking together a solution.

like image 488
Sam Avatar asked May 14 '18 20:05

Sam


1 Answers

What you could do is to have an Observable that emits when the warning popup should be displayed:

import { timer } from 'rxjs/observable/timer';
// ...
public sessionWarningTimer$ = new Subject();
// ...
setSessionTimeout() {
    // ...
    timer(sessionWarningMinutes * 60 * 1000).subscribe(this.sessionWarningTimer$);
}

In a component (e.g. your AppComponent) you could then subscribe to sessionWarningTimer$:

private destroyed$ = new Subject();

ngOnInit() {
    this
        .contextService
        .sessionWarningTimer$
        .takeUntil(this.destroyed$)
        .subscribe(() => this.displaySessionWarning());
}

ngOnDestroy() {
    this.destroyed$.next();
}

displaySessionWarning() {
    // your display code here
}

Like this, you can avoid any UI code in your service and rather focus on the warning logics.

like image 160
fjc Avatar answered Sep 28 '22 21:09

fjc