import { HttpErrorResponse, HttpEvent, HttpHandlerFn, HttpInterceptorFn, HttpRequest, } from '@angular/common/http'; import { inject } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, catchError, switchMap, throwError } from 'rxjs'; import { AuthService } from './auth.service'; export const authInterceptor: HttpInterceptorFn = ( request: HttpRequest, next: HttpHandlerFn, ): Observable> => { const auth = inject(AuthService); const router = inject(Router); const authenticatedRequest = withAccessToken(request, auth.accessToken()); return next(authenticatedRequest).pipe( catchError((error: unknown) => { if (!shouldRefresh(request, error)) { return throwError(() => error); } return auth.refreshSession().pipe( switchMap((response) => next(withAccessToken(request, response.accessToken)), ), catchError((refreshError: unknown) => { auth.logout(); void router.navigateByUrl('/login'); return throwError(() => refreshError); }), ); }), ); }; function withAccessToken( request: HttpRequest, accessToken: string | null, ): HttpRequest { if (!accessToken || !isApiRequest(request) || isAuthRequest(request)) { return request; } return request.clone({ setHeaders: { Authorization: `Bearer ${accessToken}`, }, }); } function shouldRefresh(request: HttpRequest, error: unknown): boolean { return ( isApiRequest(request) && !isAuthRequest(request) && error instanceof HttpErrorResponse && error.status === 401 ); } function isApiRequest(request: HttpRequest): boolean { return request.url.startsWith('/api/'); } function isAuthRequest(request: HttpRequest): boolean { return [ '/api/auth/login', '/api/auth/register', '/api/auth/refresh', '/api/auth/resend-verification', '/api/auth/verify-email', ].some((publicAuthUrl) => request.url.startsWith(publicAuthUrl)); }