From 92404352826eeb34f7d15b96f66dfcc1bb847bc2 Mon Sep 17 00:00:00 2001
From: Alex ORLUC <alex.orluc@maarch.org>
Date: Fri, 5 Feb 2021 11:00:53 +0100
Subject: [PATCH] FIX #16148 TIME 0:20 improve handle 401

---
 .../app/service/auth-interceptor.service.ts   | 81 +++++++++----------
 src/frontend/app/service/auth.service.ts      | 19 +++++
 2 files changed, 57 insertions(+), 43 deletions(-)

diff --git a/src/frontend/app/service/auth-interceptor.service.ts b/src/frontend/app/service/auth-interceptor.service.ts
index 6d0a6ddb1d..495f51af71 100644
--- a/src/frontend/app/service/auth-interceptor.service.ts
+++ b/src/frontend/app/service/auth-interceptor.service.ts
@@ -1,10 +1,9 @@
 import { Injectable } from '@angular/core';
 import { HttpHandler, HttpInterceptor, HttpRequest, HttpClient, HttpErrorResponse } from '@angular/common/http';
-import { Observable } from 'rxjs';
+import { BehaviorSubject, Observable } from 'rxjs';
 import { Router } from '@angular/router';
-import { catchError, switchMap } from 'rxjs/operators';
+import { catchError, filter, switchMap, take } from 'rxjs/operators';
 import { NotificationService } from './notification.service';
-import { EMPTY } from 'rxjs';
 import { SignaturesContentService } from './signatures.service';
 import { AuthService } from './auth.service';
 
@@ -24,7 +23,18 @@ export class AuthInterceptor implements HttpInterceptor {
         }
     ];
 
-    constructor(public http: HttpClient, private router: Router, public notificationService: NotificationService, public signaturesService: SignaturesContentService, public authService: AuthService) { }
+    private isRefreshing = false;
+    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(
+        null
+    );
+
+    constructor(
+        public http: HttpClient,
+        private router: Router,
+        public notificationService: NotificationService,
+        public signaturesService: SignaturesContentService,
+        public authService: AuthService
+    ) { }
 
     addAuthHeader(request: HttpRequest<any>) {
 
@@ -37,9 +47,29 @@ export class AuthInterceptor implements HttpInterceptor {
         });
     }
 
-    logout() {
-        this.authService.logout();
-        this.notificationService.error('lang.sessionExpired');
+    private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
+        if (!this.isRefreshing) {
+            this.isRefreshing = true;
+            this.refreshTokenSubject.next(null);
+
+            return this.authService.refreshToken().pipe(
+                switchMap((data: any) => {
+                    this.isRefreshing = false;
+                    this.refreshTokenSubject.next(data.token);
+                    request = this.addAuthHeader(request);
+                    return next.handle(request);
+                })
+            );
+        } else {
+            return this.refreshTokenSubject.pipe(
+                filter((token) => token != null),
+                take(1),
+                switchMap(() => {
+                    request = this.addAuthHeader(request);
+                    return next.handle(request);
+                })
+            );
+        }
     }
 
     intercept(request: HttpRequest<any>, next: HttpHandler): Observable<any> {
@@ -62,42 +92,7 @@ export class AuthInterceptor implements HttpInterceptor {
                     if (this.byPassHandleErrors.filter(url => request.url.indexOf(url.route) > -1 && url.method.indexOf(request.method) > -1).length > 0) {
                         return next.handle(request);
                     } else if (error.status === 401) {
-                        return this.http.get('../rest/authenticate/token', {
-                            params: {
-                                refreshToken: this.authService.getRefreshToken()
-                            }
-                        }).pipe(
-                            switchMap((data: any) => {
-
-                                // Update stored token
-                                this.authService.setToken(data.token);
-
-                                // Update user info
-                                this.authService.updateUserInfo(data.token);
-
-                                // Clone our request with token updated ant try to resend it
-                                request = this.addAuthHeader(request);
-
-                                return next.handle(request).pipe(
-                                    catchError(err => {
-                                        // Disconnect user if bad token process
-                                        if (err.status === 401) {
-                                            this.logout();
-                                            return EMPTY;
-                                        }
-                                    })
-                                );
-
-                            }
-                            ),
-                            catchError(err => {
-                                // Disconnect user if bad token process
-                                if (err.status === 401) {
-                                    this.logout();
-                                }
-                                return EMPTY;
-                            })
-                        );
+                        return this.handle401Error(request, next);
                     } else if (error.error.errors === 'Password expired : User must change his password') {
                         return this.router.navigate(['/password-modification']);
                     } else {
diff --git a/src/frontend/app/service/auth.service.ts b/src/frontend/app/service/auth.service.ts
index 3beff721e7..905e152350 100755
--- a/src/frontend/app/service/auth.service.ts
+++ b/src/frontend/app/service/auth.service.ts
@@ -47,6 +47,25 @@ export class AuthService {
         this.localStorage.remove('MaarchParapheurRefreshToken');
     }
 
+    refreshToken() {
+        return this.http
+            .get<any>(`../rest/authenticate/token`, { params: { refreshToken: this.getRefreshToken() } })
+            .pipe(
+                tap((data) => {
+                    // Update stored token
+                    this.setToken(data.token);
+
+                    // Update user info
+                    this.updateUserInfo(data.token);
+                }),
+                catchError((error) => {
+                    this.logout();
+                    this.notificationService.error('lang.sessionExpired');
+                    return of(false);
+                })
+            );
+    }
+
     logout() {
         const refreshToken = this.getRefreshToken();
         if (refreshToken === null) {
-- 
GitLab