refactor: refresh token
The service did not update tokens well, so it was rewritten
This commit is contained in:
parent
48a74ecbf5
commit
1f03c2a9c3
@ -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(
|
|
||||||
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$;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user