feat: add auth service
This commit is contained in:
parent
7d78295b9a
commit
86cf636e16
@ -1,10 +1,12 @@
|
|||||||
import {catchError, mergeMap, Observable, retryWhen, tap, timer} from "rxjs";
|
import {catchError, filter, mergeMap, Observable, retryWhen, switchMap, take, tap, timer} from "rxjs";
|
||||||
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
import {HttpClient, HttpErrorResponse} from "@angular/common/http";
|
||||||
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
import {NotifyColor, OpenNotifyService} from "@service/open-notify.service";
|
||||||
import {environment} from "@environment";
|
import {environment} from "@environment";
|
||||||
import {Router} from "@angular/router";
|
import {Router} from "@angular/router";
|
||||||
import {Injectable} from "@angular/core";
|
import {Injectable} from "@angular/core";
|
||||||
import {RequestBuilder, RequestData, SetRequestBuilderAfterBuild} from "@api/RequestBuilder";
|
import {RequestBuilder, RequestData, SetRequestBuilderAfterBuild} from "@api/RequestBuilder";
|
||||||
|
import {TokenRefreshService} from "@service/token-refresh.service";
|
||||||
|
import {AuthToken} from "@service/auth.service";
|
||||||
|
|
||||||
export function retryWithInterval<T>(): (source: Observable<T>) => Observable<T> {
|
export function retryWithInterval<T>(): (source: Observable<T>) => Observable<T> {
|
||||||
return (source: Observable<T>) =>
|
return (source: Observable<T>) =>
|
||||||
@ -35,13 +37,14 @@ export enum AvailableVersion {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export default abstract class ApiService implements SetRequestBuilderAfterBuild {
|
export default abstract class ApiService implements SetRequestBuilderAfterBuild {
|
||||||
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router) {
|
constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router, protected tokenRefreshService: TokenRefreshService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private apiUrl = environment.apiUrl;
|
private apiUrl = environment.apiUrl;
|
||||||
protected abstract basePath: string;
|
protected abstract basePath: string;
|
||||||
protected abstract version: AvailableVersion;
|
protected abstract version: AvailableVersion;
|
||||||
private request: RequestData = RequestBuilder.getStandardRequestData();
|
private request: RequestData = RequestBuilder.getStandardRequestData();
|
||||||
|
public static readonly tokenKey = 'auth_token';
|
||||||
|
|
||||||
public setRequestBuilder(request: RequestData): void {
|
public setRequestBuilder(request: RequestData): void {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
@ -128,6 +131,18 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
|||||||
return this.makeHttpRequest<Type>('delete');
|
return this.makeHttpRequest<Type>('delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addAuth() {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
const authToken = AuthToken.httpHeader((JSON.parse(token) as AuthToken));
|
||||||
|
authToken.keys().forEach(key => this.request.httpHeaders = this.request.httpHeaders.append(key, authToken.get(key) ?? ''))
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private handleError(error: HttpErrorResponse): void {
|
private handleError(error: HttpErrorResponse): void {
|
||||||
// todo: change to Retry-After condition
|
// todo: change to Retry-After condition
|
||||||
if (error.error.toString().includes("setup")) {
|
if (error.error.toString().includes("setup")) {
|
||||||
@ -147,6 +162,7 @@ export default abstract class ApiService implements SetRequestBuilderAfterBuild
|
|||||||
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
|
message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.';
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
|
this.router.navigate(['/login/']).then();
|
||||||
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
|
message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.';
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
|
@ -4,6 +4,7 @@ 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";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
88
src/services/auth.service.ts
Normal file
88
src/services/auth.service.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import {EventEmitter, Injectable} from '@angular/core';
|
||||||
|
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||||
|
import {catchError, Observable, of, tap} from "rxjs";
|
||||||
|
import {TokenResponse} from "@api/v1/tokenResponse";
|
||||||
|
import ApiService from "@api/api.service";
|
||||||
|
|
||||||
|
export enum AvailableAuthenticationProvider {
|
||||||
|
Bearer
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthToken {
|
||||||
|
accessToken: string;
|
||||||
|
expiresIn: Date;
|
||||||
|
authProvider: AvailableAuthenticationProvider;
|
||||||
|
endpoint: string;
|
||||||
|
|
||||||
|
constructor(accessToken: string, expiresIn: Date, authProvider: AvailableAuthenticationProvider, refreshEndpoint: string) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.expiresIn = expiresIn;
|
||||||
|
this.authProvider = authProvider;
|
||||||
|
this.endpoint = refreshEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
static httpHeader(token: AuthToken): HttpHeaders {
|
||||||
|
let header = new HttpHeaders();
|
||||||
|
|
||||||
|
if (token.authProvider === AvailableAuthenticationProvider.Bearer)
|
||||||
|
header = header.set('Authorization', `Bearer ${token.accessToken}`);
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
public expireTokenChange = new EventEmitter<Date>();
|
||||||
|
public tokenChangeError = new EventEmitter();
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setToken(token: TokenResponse, provider: AvailableAuthenticationProvider, refreshEndpoint: string) {
|
||||||
|
localStorage.setItem(ApiService.tokenKey, JSON.stringify(
|
||||||
|
new AuthToken(token.accessToken, token.expiresIn, provider, refreshEndpoint)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get tokenExpiresIn(): Date {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return new Date();
|
||||||
|
|
||||||
|
const result = new Date((JSON.parse(token) as AuthToken).expiresIn);
|
||||||
|
return result <= new Date() ? new Date() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshToken(): Observable<TokenResponse> {
|
||||||
|
const token = localStorage.getItem(ApiService.tokenKey);
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return of();
|
||||||
|
|
||||||
|
const authToken = JSON.parse(token) as AuthToken;
|
||||||
|
|
||||||
|
switch (authToken.authProvider) {
|
||||||
|
case AvailableAuthenticationProvider.Bearer:
|
||||||
|
return this.http.get<TokenResponse>(authToken.endpoint, {withCredentials: true})
|
||||||
|
.pipe(
|
||||||
|
catchError(error => {
|
||||||
|
this.tokenChangeError.emit();
|
||||||
|
throw error;
|
||||||
|
}),
|
||||||
|
tap(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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user