refactor: refresh token
The service did not update tokens well, so it was rewritten
This commit is contained in:
		| @@ -4,7 +4,6 @@ import {FooterComponent} from "@component/common/footer/footer.component"; | ||||
| import localeRu from '@angular/common/locales/ru'; | ||||
| import {registerLocaleData} from '@angular/common'; | ||||
| import {FocusNextDirective} from "@/directives/focus-next.directive"; | ||||
| import {TokenRefreshService} from "@service/token-refresh.service"; | ||||
| import {HeaderComponent} from "@component/common/header/header.component"; | ||||
|  | ||||
| @Component({ | ||||
| @@ -17,8 +16,7 @@ import {HeaderComponent} from "@component/common/header/header.component"; | ||||
|     <app-footer/>` | ||||
| }) | ||||
| export class AppComponent { | ||||
|   constructor(tokenRefreshService: TokenRefreshService) { | ||||
|   constructor() { | ||||
|     registerLocaleData(localeRu); | ||||
|     tokenRefreshService.startTokenRefresh(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import {EventEmitter, Injectable} from '@angular/core'; | ||||
| import {Injectable} from '@angular/core'; | ||||
| import {HttpClient, HttpHeaders} from "@angular/common/http"; | ||||
| import {Observable, tap} from "rxjs"; | ||||
| import {map, Observable, throwError} from "rxjs"; | ||||
| import {TokenResponse} from "@api/v1/tokenResponse"; | ||||
| import ApiService from "@api/api.service"; | ||||
|  | ||||
| @@ -21,7 +21,7 @@ export class AuthToken { | ||||
|     this.endpoint = refreshEndpoint; | ||||
|   } | ||||
|  | ||||
|   static httpHeader(token: AuthToken): HttpHeaders { | ||||
|   public static httpHeader(token: AuthToken): HttpHeaders { | ||||
|     let header = new HttpHeaders(); | ||||
|  | ||||
|     if (token.authProvider === AvailableAuthenticationProvider.Bearer) | ||||
| @@ -35,8 +35,6 @@ export class AuthToken { | ||||
|   providedIn: 'root', | ||||
| }) | ||||
| export class AuthService { | ||||
|   public expireTokenChange = new EventEmitter<Date>(); | ||||
|  | ||||
|   constructor(private http: HttpClient) { | ||||
|   } | ||||
|  | ||||
| @@ -56,12 +54,12 @@ export class AuthService { | ||||
|     return result <= new Date() ? new Date() : result; | ||||
|   } | ||||
|  | ||||
|   public refreshToken(): Observable<TokenResponse> { | ||||
|   public refreshToken(): Observable<Date> { | ||||
|     const token = localStorage.getItem(ApiService.tokenKey); | ||||
|  | ||||
|     console.log(token); | ||||
|     if (!token) | ||||
|       throw new Error("token is not found"); | ||||
|       return throwError(() => new Error("Token is not found")); | ||||
|  | ||||
|     const authToken = JSON.parse(token) as AuthToken; | ||||
|  | ||||
| @@ -69,14 +67,16 @@ export class AuthService { | ||||
|       case AvailableAuthenticationProvider.Bearer: | ||||
|         return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true}) | ||||
|           .pipe( | ||||
|             tap(response => { | ||||
|             map(response => { | ||||
|               const newExpireDate = new Date(response.expiresIn); | ||||
|               const oldExpireDate = new Date(authToken.expiresIn); | ||||
|  | ||||
|               if (newExpireDate.getTime() !== oldExpireDate.getTime()) { | ||||
|                 AuthService.setToken(response, AvailableAuthenticationProvider.Bearer, authToken.endpoint); | ||||
|                 this.expireTokenChange.emit(newExpireDate); | ||||
|                 return newExpireDate; | ||||
|               } | ||||
|  | ||||
|               return newExpireDate; | ||||
|             }) | ||||
|           ); | ||||
|     } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import {BehaviorSubject, filter, interval, Subscription, switchMap} from "rxjs"; | ||||
| import {BehaviorSubject, catchError, of, Subject, Subscription} from "rxjs"; | ||||
| import {Injectable} from "@angular/core"; | ||||
| import {AuthService} from "@service/auth.service"; | ||||
| import {environment} from "@environment"; | ||||
| @@ -8,64 +8,70 @@ import ApiService from "@api/api.service"; | ||||
|   providedIn: 'root', | ||||
| }) | ||||
| export class TokenRefreshService { | ||||
|   private tokenRefreshSubscription: Subscription | undefined; | ||||
|   private tokenRefreshing$ = new BehaviorSubject<boolean>(false); | ||||
|   private refreshTokenTimeout: any; | ||||
|   private refreshTokenExpireMs: number = environment.retryDelay; | ||||
|  | ||||
|   constructor(private authService: AuthService) { | ||||
|     this.setRefreshTokenExpireMs(AuthService.tokenExpiresIn.getTime() - 1000 - Date.now()); | ||||
|  | ||||
|     authService.expireTokenChange.subscribe(date => { | ||||
|       console.debug('Expire token change event received:', date); | ||||
|       this.setRefreshTokenExpireMs(date.getTime() - 1000 - Date.now()); | ||||
|     }); | ||||
|     this.setRefreshTokenExpireMs(AuthService.tokenExpiresIn); | ||||
|   } | ||||
|  | ||||
|   public startTokenRefresh(date: Date | null = null): void { | ||||
|     if (date) | ||||
|       this.refreshTokenExpireMs = new Date(date).getTime() - 1000 - Date.now(); | ||||
|   private startTokenRefresh(): void { | ||||
|     this.refreshTokenTimeout = setTimeout(() => { | ||||
|       this.refreshToken(); | ||||
|  | ||||
|     console.debug(this.tokenRefreshSubscription); | ||||
|     if (this.tokenRefreshSubscription && !this.tokenRefreshSubscription.closed) | ||||
|       if (this.refreshTokenExpireMs > 0) | ||||
|         this.startTokenRefresh(); | ||||
|     }, this.refreshTokenExpireMs); | ||||
|   } | ||||
|  | ||||
|   private refreshToken(): void { | ||||
|     if (this.tokenRefreshing$.value) { | ||||
|       console.warn('Refreshing token was started...'); | ||||
|       return; | ||||
|  | ||||
|     this.tokenRefreshSubscription = interval(this.refreshTokenExpireMs).pipe( | ||||
|       filter(isRefreshing => !isRefreshing), | ||||
|       switchMap(() => { | ||||
|         this.tokenRefreshing$.next(true); | ||||
|         console.debug('Send query to refresh token'); | ||||
|         return this.authService.refreshToken(); | ||||
|       }) | ||||
|     ).subscribe({ | ||||
|       next: (_) => { | ||||
|         this.tokenRefreshing$.next(false); | ||||
|       }, | ||||
|       error: error => { | ||||
|         this.tokenRefreshing$.next(false); | ||||
|         localStorage.removeItem(ApiService.tokenKey); | ||||
|     } | ||||
|  | ||||
|     console.log('Start Refreshing token...'); | ||||
|     this.tokenRefreshing$.next(true); | ||||
|  | ||||
|     this.authService.refreshToken() | ||||
|       .pipe( | ||||
|         catchError(error => { | ||||
|           console.warn('Error refreshing token', error); | ||||
|           localStorage.removeItem(ApiService.tokenKey); | ||||
|           this.refreshTokenExpireMs = -1; | ||||
|           return of(undefined); | ||||
|         })) | ||||
|       .subscribe(data => { | ||||
|         if (data) { | ||||
|           console.log('Token refreshed'); | ||||
|           this.setRefreshTokenExpireMs(data); | ||||
|         } | ||||
|         this.tokenRefreshing$.next(false); | ||||
|         console.log('Token refresh process completed'); | ||||
|       }); | ||||
|   } | ||||
|  | ||||
|   public getTokenRefreshing$(): BehaviorSubject<boolean> { | ||||
|   public getTokenRefreshing$(): Subject<boolean> { | ||||
|     return this.tokenRefreshing$; | ||||
|   } | ||||
|  | ||||
|   public stopTokenRefresh(): void { | ||||
|     if (this.tokenRefreshSubscription && !this.tokenRefreshSubscription.closed) { | ||||
|       this.tokenRefreshSubscription.unsubscribe(); | ||||
|       this.tokenRefreshSubscription = undefined; | ||||
|     } | ||||
|   } | ||||
|   public setRefreshTokenExpireMs(expireMs: number | string | Date | null = null): void { | ||||
|     let expireMsNumber: number; | ||||
|     if (expireMs === null) | ||||
|       expireMsNumber = -1; | ||||
|     else if (expireMs instanceof Date || typeof expireMs === 'string') | ||||
|       expireMsNumber = new Date(expireMs).getTime() - 1000 - Date.now(); | ||||
|     else | ||||
|       expireMsNumber = expireMs; | ||||
|  | ||||
|   public setRefreshTokenExpireMs(expireMs: number): void { | ||||
|     if (expireMs < environment.retryDelay) | ||||
|       expireMs = environment.retryDelay; | ||||
|     if (expireMsNumber < environment.retryDelay) | ||||
|       expireMsNumber = environment.retryDelay; | ||||
|  | ||||
|     this.refreshTokenExpireMs = expireMs; | ||||
|     this.refreshTokenExpireMs = expireMsNumber; | ||||
|     console.log('New refresh token interval:', this.refreshTokenExpireMs); | ||||
|  | ||||
|     console.log(expireMs); | ||||
|     this.stopTokenRefresh(); | ||||
|     clearTimeout(this.refreshTokenTimeout); | ||||
|     this.startTokenRefresh(); | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user