import React, { useEffect } from 'react'; import { useAuth } from '@clerk/clerk-react'; import { apiClient } from '../api/client'; import { setClerkAuthState } from '../utils/authGate'; interface Props { children: React.ReactNode; } /** * Injects Clerk JWT into axios and publishes auth state to the 401 gate. * Waits for Clerk isLoaded so we never attach empty auth then hard-redirect on race. */ export const ApiInterceptor: React.FC = ({ children }) => { const { getToken, isLoaded, isSignedIn } = useAuth(); useEffect(() => { setClerkAuthState({ isLoaded: Boolean(isLoaded), isSignedIn: Boolean(isSignedIn), }); }, [isLoaded, isSignedIn]); useEffect(() => { const interceptorId = apiClient.interceptors.request.use( async (config) => { // Avoid /api/me storms before Clerk finishes bootstrapping if (!isLoaded) { return config; } if (!isSignedIn) { return config; } try { const token = await getToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } } catch { // leave without Authorization — caller may get 401; gate will not bounce signed-in users } return config; }, (error) => Promise.reject(error) ); return () => { apiClient.interceptors.request.eject(interceptorId); }; }, [getToken, isLoaded, isSignedIn]); return <>{children}; };