Authentication
Configure authentication for API requests and protected specs.
chowbea-axios supports multiple authentication patterns for both your API requests and protected OpenAPI specs.
API Request Authentication
Default: localStorage Token
By default, the generated Axios instance reads a token from localStorage:
axiosInstance.interceptors.request.use((config) => {
if (typeof window !== "undefined") {
const tokenObject = localStorage.getItem("auth-token");
if (tokenObject) {
try {
const parsed = JSON.parse(tokenObject);
const token = parsed.state?.token || parsed.token || parsed;
if (typeof token === "string") {
config.headers.Authorization = `Bearer ${token}`;
}
} catch {
config.headers.Authorization = `Bearer ${tokenObject}`;
}
}
}
return config;
});This handles:
- Plain token strings
- JSON objects with
tokenproperty - Zustand-style
{ state: { token } }objects
Custom Token Key
Change the localStorage key during init:
chowbea-axios init --token-key "session-token"Or in your config:
[instance]
token_key = "session-token"Custom Token Retrieval
Modify api.instance.ts for custom logic:
import { axiosInstance } from "./api.instance";
// Example: Read from sessionStorage
axiosInstance.interceptors.request.use((config) => {
const session = sessionStorage.getItem("session");
if (session) {
const { accessToken } = JSON.parse(session);
config.headers.Authorization = `Bearer ${accessToken}`;
}
return config;
});
// Example: Read from a state management store
import { useAuthStore } from "../stores/auth";
axiosInstance.interceptors.request.use((config) => {
const token = useAuthStore.getState().token;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Example: Read from cookies (server-side)
axiosInstance.interceptors.request.use((config) => {
if (typeof document !== "undefined") {
const match = document.cookie.match(/token=([^;]+)/);
if (match) {
config.headers.Authorization = `Bearer ${match[1]}`;
}
}
return config;
});Cookie-Based Authentication
Enable credentials for cookie-based auth:
[instance]
with_credentials = trueThis sets withCredentials: true on the Axios instance, sending cookies with cross-origin requests.
API Key Authentication
For API key auth, modify the interceptor:
axiosInstance.interceptors.request.use((config) => {
config.headers["X-API-Key"] = import.meta.env.VITE_API_KEY;
return config;
});Multiple Auth Methods
Support multiple authentication methods:
axiosInstance.interceptors.request.use((config) => {
// Priority 1: Bearer token
const token = localStorage.getItem("auth-token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
return config;
}
// Priority 2: API Key
const apiKey = localStorage.getItem("api-key");
if (apiKey) {
config.headers["X-API-Key"] = apiKey;
return config;
}
// No auth
return config;
});Token Refresh
Handle expired tokens with a response interceptor:
let isRefreshing = false;
let refreshSubscribers: ((token: string) => void)[] = [];
function subscribeTokenRefresh(cb: (token: string) => void) {
refreshSubscribers.push(cb);
}
function onRefreshed(token: string) {
refreshSubscribers.forEach(cb => cb(token));
refreshSubscribers = [];
}
axiosInstance.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (isRefreshing) {
// Wait for refresh to complete
return new Promise((resolve) => {
subscribeTokenRefresh((token) => {
originalRequest.headers.Authorization = `Bearer ${token}`;
resolve(axiosInstance(originalRequest));
});
});
}
originalRequest._retry = true;
isRefreshing = true;
try {
const { data } = await axios.post("/auth/refresh", {
refreshToken: localStorage.getItem("refresh-token"),
});
localStorage.setItem("auth-token", data.accessToken);
onRefreshed(data.accessToken);
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
return axiosInstance(originalRequest);
} catch (refreshError) {
// Refresh failed, redirect to login
window.location.href = "/login";
return Promise.reject(refreshError);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);Protected OpenAPI Specs
For specs that require authentication to access:
Static Token
[fetch]
headers = { Authorization = "Bearer your-static-token" }Environment Variable
[fetch]
headers = { Authorization = "Bearer $API_TOKEN" }Then set the variable before running:
export API_TOKEN="your-secret-token"
chowbea-axios fetchMultiple Headers
[fetch]
headers = {
Authorization = "Bearer $API_TOKEN",
X-API-Key = "$API_KEY"
}CI/CD Setup
In your CI pipeline:
jobs:
build:
steps:
- name: Generate API types
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: chowbea-axios fetchHandling 401 Errors
The generated error handling includes an UNAUTHORIZED code:
const { data, error } = await api.get("/protected-resource");
if (error) {
if (error.code === "UNAUTHORIZED") {
// Token expired or invalid
localStorage.removeItem("auth-token");
window.location.href = "/login";
return;
}
}Security Best Practices
- Never commit tokens - Use environment variables
- Use HTTPS - Always in production
- Set appropriate CORS - Only allow your domains
- Implement token refresh - Don't use long-lived tokens
- Clear tokens on logout - Clean up localStorage/cookies