generated from nhcarrigan/template
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
/**
|
|
* @copyright 2026 NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import { Injectable, inject } from '@angular/core';
|
|
import {
|
|
HttpRequest,
|
|
HttpHandler,
|
|
HttpEvent,
|
|
HttpInterceptor,
|
|
HttpErrorResponse,
|
|
HttpClient
|
|
} from '@angular/common/http';
|
|
import { Observable, catchError, throwError, switchMap, BehaviorSubject, filter, take } from 'rxjs';
|
|
import { Router } from '@angular/router';
|
|
import { environment } from '../../environments/environment';
|
|
|
|
@Injectable()
|
|
export class AuthInterceptor implements HttpInterceptor {
|
|
private isRefreshing = false;
|
|
private refreshTokenSubject = new BehaviorSubject<boolean | null>(null);
|
|
|
|
constructor(
|
|
private router: Router,
|
|
private http: HttpClient
|
|
) {}
|
|
|
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
|
// Clone the request to add withCredentials
|
|
const authReq = request.clone({
|
|
withCredentials: true
|
|
});
|
|
|
|
return next.handle(authReq).pipe(
|
|
catchError((error: HttpErrorResponse) => {
|
|
if (error.status === 401 && !request.url.includes('/auth/refresh') && !request.url.includes('/auth/logout')) {
|
|
return this.handle401Error(authReq, next);
|
|
}
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
private handle401Error(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
|
if (!this.isRefreshing) {
|
|
this.isRefreshing = true;
|
|
this.refreshTokenSubject.next(null);
|
|
|
|
return this.http.post(
|
|
`${environment.apiUrl}/auth/refresh`,
|
|
{},
|
|
{ withCredentials: true }
|
|
).pipe(
|
|
switchMap(() => {
|
|
this.isRefreshing = false;
|
|
this.refreshTokenSubject.next(true);
|
|
return next.handle(request);
|
|
}),
|
|
catchError((err) => {
|
|
this.isRefreshing = false;
|
|
this.refreshTokenSubject.next(false);
|
|
this.router.navigate(['/']);
|
|
return throwError(() => err);
|
|
})
|
|
);
|
|
}
|
|
|
|
return this.refreshTokenSubject.pipe(
|
|
filter(result => result !== null),
|
|
take(1),
|
|
switchMap(success => {
|
|
if (success) {
|
|
return next.handle(request);
|
|
}
|
|
return throwError(() => new Error('Token refresh failed'));
|
|
})
|
|
);
|
|
}
|
|
} |