import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http'; import { inject } from '@angular/core'; import { AuthService } from '../services/auth.service'; import { catchError, switchMap } from 'rxjs/operators'; import { throwError, of } from 'rxjs'; export const authInterceptor: HttpInterceptorFn = (req, next) => { const auth = inject(AuthService); // Attach Authorization header when we have a token const token = auth.getAccessToken(); let authReq = req; const isRefreshCall = req.url.includes('/auth/refresh'); const isApiCall = req.url.startsWith('/proxy/api') || req.url.startsWith('/api'); // Ensure cookies are sent for API calls (dev proxy and prod) if (req.url.startsWith('/proxy/api') || req.url.startsWith('/api')) { authReq = authReq.clone({ withCredentials: true }); } if (token && isApiCall) { authReq = authReq.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next(authReq).pipe( catchError((err) => { if (!isApiCall) { // Do not attempt refresh for non-API requests (e.g., YouTube/Twitch/Rumble) return throwError(() => err); } if (err instanceof HttpErrorResponse && err.status === 401) { // If the 401 is for the refresh endpoint itself, do NOT try to refresh again. if (isRefreshCall) { return throwError(() => err); } // If there is no access token at all, we are not logged in: do not try refresh const hasToken = !!auth.getAccessToken(); if (!hasToken) { return throwError(() => err); } // Try a single refresh then retry the request once return auth.refresh().pipe( switchMap((ok) => { if (!ok) return throwError(() => err); const newToken = auth.getAccessToken(); let retry = authReq; if (newToken) { retry = retry.clone({ setHeaders: { Authorization: `Bearer ${newToken}` } }); } if (retry.url.startsWith('/proxy/api')) { retry = retry.clone({ withCredentials: true }); } return next(retry); }) ); } return throwError(() => err); }) ); };