portfolio-admin/src/app/interceptors/auth-interceptor.ts

102 lines
2.6 KiB
TypeScript

import { inject } from '@angular/core';
import {
HttpRequest,
HttpHandlerFn,
HttpEvent,
HttpErrorResponse,
HttpInterceptorFn
} from '@angular/common/http';
import { BehaviorSubject, catchError, filter, Observable, switchMap, take, throwError } from 'rxjs';
import { AuthService } from '../auth/auth.service';
let refreshInProgress = false;
const refreshSubject = new BehaviorSubject<string | null>(null);
export const AuthInterceptor: HttpInterceptorFn = (
req: HttpRequest<unknown>,
next: HttpHandlerFn
): Observable<HttpEvent<unknown>> => {
const authSvc: AuthService = inject(AuthService);
let headers = req.headers;
// add API key
const apiKey = authSvc.getApiKey();
if (apiKey) {
headers = headers.set('XApiKey', apiKey);
}
// add content type if needed
if (!(req.body instanceof FormData) &&
(req.method === 'POST' || req.method === 'PUT') &&
!headers.has('Content-Type')) {
headers = headers.set('Content-Type', 'application/json');
}
// add access token
const token = authSvc.currentToken;
if (token) {
headers = headers.set('Authorization', `Bearer ${token}`);
}
// final cloned request
const clonedRequest = req.clone({
headers,
withCredentials: true
});
return next(clonedRequest).pipe(
catchError((err: HttpErrorResponse) => {
if (err.status !== 401) {
return throwError(() => err);
}
// if refresh is already in progress
if (refreshInProgress) {
return refreshSubject.pipe(
filter(t => t !== null),
take(1),
switchMap(newToken => {
const retryReq = clonedRequest.clone({
headers: clonedRequest.headers.set('Authorization', `Bearer ${newToken}`),
withCredentials: true
});
return next(retryReq);
})
);
}
// start refresh
refreshInProgress = true;
refreshSubject.next(null);
return authSvc.refreshToken()!.pipe(
switchMap(res => {
refreshInProgress = false;
const newToken = res.accessToken;
authSvc.safeSetToken(newToken);
refreshSubject.next(newToken);
const retryReq = clonedRequest.clone({
headers: clonedRequest.headers.set('Authorization', `Bearer ${newToken}`),
withCredentials: true
});
return next(retryReq);
}),
catchError(refreshErr => {
refreshInProgress = false;
refreshSubject.next(null);
authSvc.logout();
return throwError(() => refreshErr);
})
);
})
);
};