Authentication
Intrig uses Axios under the hood. Authentication is configured through the IntrigProvider configs, which accept all Axios configuration options plus interceptors for dynamic auth scenarios.
Configuration layers:
configs.defaults— applies to all API sourcesconfigs[sourceName]— source-specific configuration (merges with/overrides defaults)
Configuration Interface
interface DefaultConfigs extends CreateAxiosDefaults {
debounceDelay?: number;
requestInterceptor?: (
config: InternalAxiosRequestConfig,
) => Promise<InternalAxiosRequestConfig>;
responseInterceptor?: (
config: AxiosResponse<any>,
) => Promise<AxiosResponse<any>>;
}
Since DefaultConfigs extends Axios's CreateAxiosDefaults, all standard Axios options are available:
headers— default headers for all requestsbaseURL— base URL for requeststimeout— request timeoutwithCredentials— send cookies cross-origin- And all other Axios config options
Static Authentication
For APIs with static credentials (API keys, fixed tokens):
API Key in Header
<IntrigProvider
configs={{
my_api: {
headers: {
'X-API-Key': 'your-api-key',
},
},
}}
>
{children}
</IntrigProvider>
Bearer Token
<IntrigProvider
configs={{
my_api: {
headers: {
Authorization: `Bearer ${token}`,
},
},
}}
>
{children}
</IntrigProvider>
Basic Auth
<IntrigProvider
configs={{
my_api: {
auth: {
username: 'user',
password: 'pass',
},
},
}}
>
{children}
</IntrigProvider>
Dynamic Authentication
For tokens that change (user sessions, refresh tokens), use requestInterceptor:
Token from Storage
<IntrigProvider
configs={{
my_api: {
async requestInterceptor(config) {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
},
}}
>
{children}
</IntrigProvider>
Token from Auth Context
function AuthenticatedProvider({ children }: { children: React.ReactNode }) {
const { getAccessToken } = useAuth();
const configs = useMemo(() => ({
my_api: {
async requestInterceptor(config) {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
},
},
}), [getAccessToken]);
return (
<IntrigProvider configs={configs}>
{children}
</IntrigProvider>
);
}
Token Refresh
Use responseInterceptor to handle token refresh on 401:
function AuthenticatedProvider({ children }: { children: React.ReactNode }) {
const { getAccessToken, refreshToken } = useAuth();
const configs = useMemo(() => ({
my_api: {
async requestInterceptor(config) {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
},
async responseInterceptor(response) {
// Success responses pass through
return response;
},
},
}), [getAccessToken]);
return (
<IntrigProvider configs={configs}>
{children}
</IntrigProvider>
);
}
The response interceptor only handles successful responses. For 401 handling, you'll need to catch errors at the hook level or use a central error handler:
function useAuthErrorHandler() {
const errors = useCentralError();
const { refreshToken, logout } = useAuth();
useEffect(() => {
const authError = errors.find(
e => isHttpError(e.error) && e.error.status === 401
);
if (authError) {
refreshToken().catch(() => logout());
}
}, [errors]);
}
Multiple API Sources
Configure different auth for each source:
<IntrigProvider
configs={{
// Shared defaults
defaults: {
timeout: 30000,
},
// Public API — API key
public_api: {
headers: {
'X-API-Key': process.env.REACT_APP_PUBLIC_API_KEY,
},
},
// Internal API — Bearer token
internal_api: {
async requestInterceptor(config) {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
},
},
// Third-party API — Basic auth
vendor_api: {
auth: {
username: process.env.REACT_APP_VENDOR_USER,
password: process.env.REACT_APP_VENDOR_PASS,
},
},
}}
>
{children}
</IntrigProvider>
Interceptor Chain
When both defaults and source-specific interceptors are defined, they chain:
defaults.requestInterceptorruns first- Source-specific
requestInterceptorruns second (receives output of step 1)
<IntrigProvider
configs={{
defaults: {
async requestInterceptor(config) {
// Runs first — add correlation ID to all requests
config.headers['X-Correlation-ID'] = generateCorrelationId();
return config;
},
},
my_api: {
async requestInterceptor(config) {
// Runs second — config already has correlation ID
config.headers.Authorization = `Bearer ${token}`;
return config;
},
},
}}
>
{children}
</IntrigProvider>
The same chaining applies to responseInterceptor.
Cookies and Credentials
For cookie-based auth (session cookies, CSRF):
<IntrigProvider
configs={{
my_api: {
withCredentials: true, // Send cookies cross-origin
headers: {
'X-CSRF-Token': csrfToken,
},
},
}}
>
{children}
</IntrigProvider>
Common Patterns
Wrapper Component for Authenticated Sections
function AuthenticatedApp() {
const { isAuthenticated, token } = useAuth();
if (!isAuthenticated) {
return <LoginPage />;
}
return (
<IntrigProvider
configs={{
my_api: {
headers: {
Authorization: `Bearer ${token}`,
},
},
}}
>
<AppRoutes />
</IntrigProvider>
);
}
Re-mounting Provider on Token Change
If your token changes and you need to reset all cached state:
function AuthenticatedApp() {
const { token } = useAuth();
// Key change forces remount, clearing all state
return (
<IntrigProvider
key={token}
configs={{
my_api: {
headers: {
Authorization: `Bearer ${token}`,
},
},
}}
>
<AppRoutes />
</IntrigProvider>
);
}