|
1 | 1 | // Modified from https://jasonwatmore.com/post/2022/11/15/angular-14-jwt-authentication-example-tutorial#login-component-ts
|
2 |
| -import { Injectable } from '@angular/core'; |
| 2 | +import { DestroyRef, inject, Injectable } from '@angular/core'; |
3 | 3 | import { Router } from '@angular/router';
|
4 | 4 | import { HttpClient } from '@angular/common/http';
|
5 |
| -import { BehaviorSubject, Observable } from 'rxjs'; |
| 5 | +import { BehaviorSubject, Observable, timer } from 'rxjs'; |
| 6 | +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
6 | 7 | import { map, switchMap } from 'rxjs/operators';
|
7 | 8 | import { UServRes } from '../_models/user.service.model';
|
8 | 9 | import { User } from '../_models/user.model';
|
9 | 10 | import { ApiService } from './api.service';
|
| 11 | +import { ToastService } from './toast.service'; |
10 | 12 |
|
11 | 13 | @Injectable({ providedIn: 'root' })
|
12 | 14 | export class AuthenticationService extends ApiService {
|
13 | 15 | protected apiPath = 'user';
|
14 | 16 |
|
| 17 | + private destroyRef = inject(DestroyRef); |
| 18 | + |
15 | 19 | private userSubject: BehaviorSubject<User | null>;
|
16 | 20 | public user$: Observable<User | null>;
|
17 | 21 |
|
18 | 22 | constructor(
|
19 | 23 | private router: Router,
|
20 | 24 | private http: HttpClient,
|
| 25 | + private toastService: ToastService, |
21 | 26 | ) {
|
22 | 27 | super();
|
23 | 28 | const userData = localStorage.getItem('user');
|
@@ -53,6 +58,8 @@ export class AuthenticationService extends ApiService {
|
53 | 58 | }
|
54 | 59 | localStorage.setItem('user', JSON.stringify(user));
|
55 | 60 | this.userSubject.next(user);
|
| 61 | + this.startTokenExpiryCheck(); |
| 62 | + |
56 | 63 | return user;
|
57 | 64 | }),
|
58 | 65 | );
|
@@ -119,4 +126,41 @@ export class AuthenticationService extends ApiService {
|
119 | 126 | }),
|
120 | 127 | );
|
121 | 128 | }
|
| 129 | + |
| 130 | + displaySessionExpiryWarning(): void { |
| 131 | + this.toastService.showToast('Your session will expire in less than 5 minutes. Please log in again.'); |
| 132 | + } |
| 133 | + |
| 134 | + public startTokenExpiryCheck(): void { |
| 135 | + const tokenExpirationTime = this.getTokenExpiration(); |
| 136 | + if (!tokenExpirationTime) { |
| 137 | + this.logout(); |
| 138 | + return; |
| 139 | + } |
| 140 | + |
| 141 | + const oneMinute = 60 * 1000; |
| 142 | + const timeLeft = tokenExpirationTime - Date.now(); |
| 143 | + if (timeLeft > 5 * oneMinute) { |
| 144 | + timer(timeLeft - 5 * oneMinute) |
| 145 | + .pipe(takeUntilDestroyed(this.destroyRef)) |
| 146 | + .subscribe(() => this.displaySessionExpiryWarning()); |
| 147 | + } else { |
| 148 | + this.displaySessionExpiryWarning(); |
| 149 | + } |
| 150 | + |
| 151 | + timer(timeLeft) |
| 152 | + .pipe(takeUntilDestroyed(this.destroyRef)) |
| 153 | + .subscribe(() => { |
| 154 | + alert('Your session has expired. Please log in again.'); |
| 155 | + this.logout(); |
| 156 | + }); |
| 157 | + } |
| 158 | + |
| 159 | + private getTokenExpiration() { |
| 160 | + const user = this.userValue; |
| 161 | + if (!user || !user.accessToken) return null; |
| 162 | + |
| 163 | + const tokenPayload = JSON.parse(atob(user.accessToken.split('.')[1])); |
| 164 | + return tokenPayload.exp ? tokenPayload.exp * 1000 : null; |
| 165 | + } |
122 | 166 | }
|
0 commit comments