Merge pull request #263 in TEA/mina-sidor-fa-web from feature/TV-892-inactivity-check to develop

Squashed commit of the following:

commit 0a6b323e5d8d94c18ebada2cdb47a6a48f0f32bd
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Fri Nov 12 14:20:21 2021 +0100

    Changed variable name after PR

commit 1cb495e7db4365720c1276959d77bc1f6d460c6f
Merge: 2bfe34ac b5071fcf
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Fri Nov 12 14:18:11 2021 +0100

    Merge branch 'develop' into feature/TV-892-inactivity-check

commit 2bfe34acc5eb5e2c6260624de7c0d0c9c5a1728b
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Fri Nov 12 13:45:31 2021 +0100

    Added check on last activity after application close
This commit is contained in:
Erik Tiekstra
2021-11-12 14:25:35 +01:00
parent a41b42d08c
commit d88e30e24c
3 changed files with 36 additions and 12 deletions

View File

@@ -1,7 +1,8 @@
import { DOCUMENT } from '@angular/common'; 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 { Title } from '@angular/platform-browser';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { APPLICATION_CLOSED_TIME_STAMP } from '@msfa-constants/local-storage-keys';
import { environment } from '@msfa-environment'; import { environment } from '@msfa-environment';
import { AuthenticationService } from '@msfa-services/api/authentication.service'; import { AuthenticationService } from '@msfa-services/api/authentication.service';
import { IdleService } from '@msfa-services/api/idle.service'; import { IdleService } from '@msfa-services/api/idle.service';
@@ -18,6 +19,13 @@ export class AppComponent {
userIsIdle$: Observable<boolean> = this.idleService.isIdle$; userIsIdle$: Observable<boolean> = this.idleService.isIdle$;
timeLeftBeforeLogout$: Observable<string> = this.idleService.timeLeftBeforeLogout$; timeLeftBeforeLogout$: Observable<string> = 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( constructor(
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
private router: Router, private router: Router,

View File

@@ -2,10 +2,12 @@ export const AUTH_TOKEN_KEY = 'id_token';
export const AUTH_TOKEN_EXPIRE_KEY = 'expires_at'; export const AUTH_TOKEN_EXPIRE_KEY = 'expires_at';
export const AUTH_TOKEN_EXPIRES_IN_KEY = 'expires_in'; export const AUTH_TOKEN_EXPIRES_IN_KEY = 'expires_in';
export const SELECTED_ORGANIZATION_NUMBER_KEY = 'selected_orgnr'; export const SELECTED_ORGANIZATION_NUMBER_KEY = 'selected_orgnr';
export const APPLICATION_CLOSED_TIME_STAMP = 'application_closed';
export const ALL_LOCAL_STORAGE_KEYS = [ export const ALL_LOCAL_STORAGE_KEYS = [
AUTH_TOKEN_KEY, AUTH_TOKEN_KEY,
AUTH_TOKEN_EXPIRE_KEY, AUTH_TOKEN_EXPIRE_KEY,
AUTH_TOKEN_EXPIRES_IN_KEY, AUTH_TOKEN_EXPIRES_IN_KEY,
SELECTED_ORGANIZATION_NUMBER_KEY, SELECTED_ORGANIZATION_NUMBER_KEY,
APPLICATION_CLOSED_TIME_STAMP,
]; ];

View File

@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { import {
ALL_LOCAL_STORAGE_KEYS, ALL_LOCAL_STORAGE_KEYS,
APPLICATION_CLOSED_TIME_STAMP,
AUTH_TOKEN_EXPIRES_IN_KEY, AUTH_TOKEN_EXPIRES_IN_KEY,
AUTH_TOKEN_EXPIRE_KEY, AUTH_TOKEN_EXPIRE_KEY,
AUTH_TOKEN_KEY, AUTH_TOKEN_KEY,
@@ -10,7 +11,7 @@ import {
import { environment } from '@msfa-environment'; import { environment } from '@msfa-environment';
import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model'; import { AuthenticationResponse } from '@msfa-models/api/authentication.response.model';
import { Authentication, mapAuthApiResponseToAuthenticationResult } from '@msfa-models/authentication.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 { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import { catchError, distinctUntilChanged, filter, map, tap } from 'rxjs/operators'; import { catchError, distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
@@ -37,8 +38,19 @@ export class AuthenticationService {
]).pipe( ]).pipe(
filter(([token, expiresAt]) => !!(token && expiresAt)), filter(([token, expiresAt]) => !!(token && expiresAt)),
map(([, 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 { return {
isValid: isBefore(new Date(), sub(expiresAt, { minutes: 1 })), isValid,
isRefreshable: false, isRefreshable: false,
// isRefreshable: isBefore(new Date(), expiresAt), // isRefreshable: isBefore(new Date(), expiresAt),
}; };
@@ -102,16 +114,18 @@ export class AuthenticationService {
); );
} }
private get _localStorageData(): { id_token: string; expires_at: number; expires_in: number } { private get _localStorageData(): { idToken: string; expiresAt: number; expiresIn: number; lastActive: number } {
const id_token = localStorage.getItem(AUTH_TOKEN_KEY); const idToken = localStorage.getItem(AUTH_TOKEN_KEY);
const expiresAt = localStorage.getItem(AUTH_TOKEN_EXPIRE_KEY); const expiresAt = localStorage.getItem(AUTH_TOKEN_EXPIRE_KEY);
const expiresIn = localStorage.getItem(AUTH_TOKEN_EXPIRES_IN_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, idToken,
expires_at: +JSON.parse(expiresAt), expiresAt: +expiresAt,
expires_in: +expiresIn, expiresIn: +expiresIn,
lastActive: +lastActive,
} }
: null; : null;
} }
@@ -130,9 +144,9 @@ export class AuthenticationService {
const localStorageData = this._localStorageData; const localStorageData = this._localStorageData;
if (localStorageData) { if (localStorageData) {
this._token$.next(localStorageData.id_token); this._token$.next(localStorageData.idToken);
this._expiresAt$.next(localStorageData.expires_at); this._expiresAt$.next(localStorageData.expiresAt);
this._expiresIn$.next(localStorageData.expires_in); this._expiresIn$.next(localStorageData.expiresIn);
} }
} }