Auth-Aware Fetch
Extend fetchWithCache to attach auth credentials and handle 401 responses.
Auth-Aware Fetch
Prerequisites: Auth Store set up, API Integration fetchWithCache in place.
How the CLI Generates This
Set features.auth.type in swoff.config.json:
type value | withAuthHeaders() behavior | authenticatedFetch needed? |
|---|---|---|
"cookie" | No-op — browser auto-sends httpOnly cookies | Yes (401 handling, user mgmt) |
"bearer" | Injects Authorization: Bearer <token> | Yes |
"custom" | Editable stub — you implement your own header | Yes |
Generated Code
import { fetchWithCache } from "./fetch-wrapper.js";
import { getAuth, clearAuth } from "./auth-store.js";
function withAuthHeaders(headers, auth) {
// CLI generates this based on config: features.auth.type
// "cookie": return headers (no-op)
// "bearer": if (auth?.token) headers.set("Authorization", `Bearer ${auth.token}`)
// "custom": // EDIT THIS — implement your custom header logic
return headers;
}
function isAuthUrl(url) {
const authPaths = [
"/login", "/logout", "/register",
"/api/login", "/api/logout", "/api/register",
"/api/refresh", "/api/me",
];
return authPaths.some((path) => url.includes(path));
}
export async function authenticatedFetch(input, options = {}) {
const auth = await getAuth();
const headers = new Headers(options.headers);
withAuthHeaders(headers, auth);
const url = typeof input === "string" ? input : input.url;
if (isAuthUrl(url) && !headers.has("X-SW-Cache-Strategy")) {
headers.set("X-SW-Cache-Strategy", "mutation");
}
const response = await fetchWithCache(input, { ...options, headers });
if (response.status === 401) {
await clearAuth();
window.dispatchEvent(new CustomEvent("sw-auth-unauthorized"));
}
return response;
}Usage
import { authenticatedFetch } from "./swoff/auth-fetch.js";
// Authenticated read — cached by SW for offline
const profile = await authenticatedFetch("/api/me").then((r) => r.json());
// Authenticated mutation — passes through to server
await authenticatedFetch("/api/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Hello" }),
});
// Login — bypasses SW cache entirely
await authenticatedFetch("/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
});Every call to authenticatedFetch() runs getAuth() internally. After login, getAuth() returns the token from memory (instant). No manual token passing needed.
Token Refresh (Optional)
The CLI generates ensureValidAuth() inside swoff/auth-fetch.js when features.auth.refreshPath is set. Call it before any request that may need a fresh token:
import { ensureValidAuth } from "./swoff/auth-fetch.js";
const auth = await ensureValidAuth();
if (!auth) {
// Token refresh failed, redirect to login
return;
}
const data = await authenticatedFetch("/api/sensitive").then((r) => r.json());Changing withAuthHeaders Based on Your Auth Type
Check Auth Libraries Reference to determine your category:
| Auth Category | withAuthHeaders() | Example |
|---|---|---|
| Cookie/Session | No-op (return headers as-is) | Better-Auth default, Auth.js |
| Bearer Token | headers.set("Authorization", "Bearer " + auth.token) | Auth0, Firebase, Supabase raw fetch |
| Custom Header | Set your custom header name | DRF TokenAuth, API keys |
| SDK-managed | Skip this file, use SDK's client | Supabase JS SDK, Better-Auth client |
Next Steps
- Auth Libraries Reference — identify your auth type
- Current User — fetch and cache user data
Swoff