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