diff --git a/apps/mina-sidor-fa/src/app/app.component.ts b/apps/mina-sidor-fa/src/app/app.component.ts index 8131cef..cf3fc21 100644 --- a/apps/mina-sidor-fa/src/app/app.component.ts +++ b/apps/mina-sidor-fa/src/app/app.component.ts @@ -1,7 +1,8 @@ import { DOCUMENT } from '@angular/common'; -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostListener, Inject } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { APPLICATION_CLOSED_TIME_STAMP } from '@msfa-constants/local-storage-keys'; import { environment } from '@msfa-environment'; import { AuthenticationService } from '@msfa-services/api/authentication.service'; import { IdleService } from '@msfa-services/api/idle.service'; @@ -18,6 +19,13 @@ export class AppComponent { userIsIdle$: Observable = this.idleService.isIdle$; timeLeftBeforeLogout$: Observable = this.idleService.timeLeftBeforeLogout$; + // Saving latest activity timestamp when application is closed + // to avoid users be automatically logged in if user hasn't used the application for a while. + @HostListener('window:beforeunload') + saveApplicationClosedTimestamp(): void { + localStorage.setItem(APPLICATION_CLOSED_TIME_STAMP, new Date().getTime().toString()); + } + constructor( @Inject(DOCUMENT) private document: Document, private router: Router, diff --git a/apps/mina-sidor-fa/src/app/shared/constants/local-storage-keys.ts b/apps/mina-sidor-fa/src/app/shared/constants/local-storage-keys.ts index 1d1229f..c89d094 100644 --- a/apps/mina-sidor-fa/src/app/shared/constants/local-storage-keys.ts +++ b/apps/mina-sidor-fa/src/app/shared/constants/local-storage-keys.ts @@ -2,10 +2,12 @@ export const AUTH_TOKEN_KEY = 'id_token'; export const AUTH_TOKEN_EXPIRE_KEY = 'expires_at'; export const AUTH_TOKEN_EXPIRES_IN_KEY = 'expires_in'; export const SELECTED_ORGANIZATION_NUMBER_KEY = 'selected_orgnr'; +export const APPLICATION_CLOSED_TIME_STAMP = 'application_closed'; export const ALL_LOCAL_STORAGE_KEYS = [ AUTH_TOKEN_KEY, AUTH_TOKEN_EXPIRE_KEY, AUTH_TOKEN_EXPIRES_IN_KEY, SELECTED_ORGANIZATION_NUMBER_KEY, + APPLICATION_CLOSED_TIME_STAMP, ]; diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts index cd5537d..dc2542a 100644 --- a/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts +++ b/apps/mina-sidor-fa/src/app/shared/services/api/authentication.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { ALL_LOCAL_STORAGE_KEYS, + APPLICATION_CLOSED_TIME_STAMP, AUTH_TOKEN_EXPIRES_IN_KEY, AUTH_TOKEN_EXPIRE_KEY, AUTH_TOKEN_KEY, @@ -10,7 +11,7 @@ import { import { environment } from '@msfa-environment'; import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model'; import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.model'; -import { add, isBefore, sub } from 'date-fns'; +import { add, isAfter, isBefore, sub } from 'date-fns'; import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { catchError, distinctUntilChanged, filter, map, tap } from 'rxjs/operators'; @@ -37,8 +38,19 @@ export class AuthenticationService { ]).pipe( filter(([token, expiresAt]) => !!(token && expiresAt)), map(([, expiresAt]) => { + const now = new Date(); + const applicationClosedTimeStamp = localStorage.getItem(APPLICATION_CLOSED_TIME_STAMP); + // Checking to see if the user has been active on the page within the last hour + const applicationClosedWithin1Hour = + !applicationClosedTimeStamp || isAfter(+applicationClosedTimeStamp, sub(now, { hours: 1 })); + const isValid = isBefore(now, sub(expiresAt, { minutes: 1 })) && applicationClosedWithin1Hour; + + if (applicationClosedTimeStamp) { + localStorage.removeItem(APPLICATION_CLOSED_TIME_STAMP); + } + return { - isValid: isBefore(new Date(), sub(expiresAt, { minutes: 1 })), + isValid, isRefreshable: false, // isRefreshable: isBefore(new Date(), expiresAt), }; @@ -102,16 +114,18 @@ export class AuthenticationService { ); } - private get _localStorageData(): { id_token: string; expires_at: number; expires_in: number } { - const id_token = localStorage.getItem(AUTH_TOKEN_KEY); + private get _localStorageData(): { idToken: string; expiresAt: number; expiresIn: number; lastActive: number } { + const idToken = localStorage.getItem(AUTH_TOKEN_KEY); const expiresAt = localStorage.getItem(AUTH_TOKEN_EXPIRE_KEY); const expiresIn = localStorage.getItem(AUTH_TOKEN_EXPIRES_IN_KEY); + const lastActive = localStorage.getItem(APPLICATION_CLOSED_TIME_STAMP); - return id_token && expiresAt && expiresIn + return idToken && expiresAt && expiresIn ? { - id_token, - expires_at: +JSON.parse(expiresAt), - expires_in: +expiresIn, + idToken, + expiresAt: +expiresAt, + expiresIn: +expiresIn, + lastActive: +lastActive, } : null; } @@ -130,9 +144,9 @@ export class AuthenticationService { const localStorageData = this._localStorageData; if (localStorageData) { - this._token$.next(localStorageData.id_token); - this._expiresAt$.next(localStorageData.expires_at); - this._expiresIn$.next(localStorageData.expires_in); + this._token$.next(localStorageData.idToken); + this._expiresAt$.next(localStorageData.expiresAt); + this._expiresIn$.next(localStorageData.expiresIn); } }