Swoff Swoff

Auth Libraries Reference

How popular auth libraries transmit identity under the hood — and whether you need to set headers.

Auth Libraries Reference

Identify your auth library's mechanism to know whether you need to configure withAuthHeaders() in Auth-Aware Fetch or can skip it entirely.

The Three Categories

The server sets an http-only cookie on login. The browser automatically sends it on every same-origin request. No header setup needed.

Set features.auth.type: "cookie" in swoff.config.json — the CLI generates a no-op withAuthHeaders().

Bearer Token Auth (Manual)

The server returns a token string on login. The client must store it and manually set Authorization: Bearer <token> on every request. Header setup handled by CLI.

Set features.auth.type: "bearer" — the CLI generates withAuthHeaders() with the bearer injection.

Custom Header Auth (Manual)

Same as Bearer, but with a non-standard header name (e.g., X-Auth-Token, X-API-Key, Authorization: Token). You must edit the generated stub.

Set features.auth.type: "custom" — the CLI generates a withAuthHeaders() stub you edit.

Quick Reference Table

Library / FrameworkCategoryTransmissionNeed to Edit withAuthHeaders()?
Better-Auth (default)Cookiebetter-auth.session-token http-only cookie (HMAC-signed)No
Better-Auth (bearer plugin)BearerAuthorization: Bearer <session_token>Yes
Auth.js / NextAuth.js (v4 & v5)Cookienext-auth.session-token / __Secure-authjs.session-token http-only encrypted JWTNo
Clerk (same-origin)Cookie__session cookie (set via JS on app domain)No
Clerk (cross-origin)BearerAuthorization: Bearer <token> from getToken()Yes
Supabase (browser SDK)SDK-managedAuthorization: Bearer <JWT> — auto-injected by SDK clientNo (use SDK client)
Supabase (raw fetch)BearerAuthorization: Bearer <JWT>Yes
Supabase (SSR mode)Cookiesb-{ref}-auth-token http-only cookieNo
Auth0 SPA SDKBearerAuthorization: Bearer <token> from getTokenSilently()Yes (always)
Firebase AuthBearerAuthorization: Bearer <id_token> from getIdToken()Yes (always)
Laravel Sanctum (SPA mode)Cookielaravel_session http-only cookie + X-XSRF-TOKEN CSRF headerNo (CSRF only)
Laravel Sanctum (API mode)BearerAuthorization: Bearer <personal_access_token>Yes
DRF SessionAuthenticationCookiesessionid http-only cookie + X-CSRFToken headerNo (CSRF only)
DRF TokenAuthenticationCustom HeaderAuthorization: Token <token>Yes
DRF SimpleJWTBearerAuthorization: Bearer <JWT>Yes
Passport.js (session strategy)Cookieconnect.sid http-only cookie (via express-session)No
Passport.js (JWT strategy)Bearer / ConfigurableTypically Authorization: Bearer <JWT>Yes
Logto (web / SSR)Cookielogto-prefixed http-only encrypted cookieNo
Logto (SPA / API)BearerAuthorization: Bearer <token> from getAccessToken()Yes

How to Determine Your Category

Not sure which one you have? Inspect your login flow:

Check the Login Response

Open DevTools → Network tab → Find your login request → Look at the response headers:

Set-Cookie: better-auth.session-token=abc123; HttpOnly; Secure; SameSite=Lax

                    http-only cookie present?

Yes, http-only cookie present → Cookie/Session auth.

Your browser will auto-send this cookie on subsequent same-origin requests. You don't need to edit withAuthHeaders().

Check the Login Response Body

If no Set-Cookie header, check the JSON body:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": { "id": 1, "name": "Alice" }
}

Token field in response → Bearer or Custom header auth.

  • If your backend uses Authorization: Bearer <token> → Bearer category
  • If it uses Authorization: Token <token> or X-API-Key → Custom header category
  • Either way, you must edit withAuthHeaders()

Check if You're Using an SDK

Are you calling supabase.from('notes').select() or authClient.fetch('/api/...') instead of raw fetch()?

Yes → SDK-managed auth. The SDK auto-injects headers. You don't need authenticatedFetch — but you also lose Swoff's caching and offline queue. To get both, either:

  1. Use authenticatedFetch with the token you extract from the SDK (via supabase.auth.getSession() or similar)
  2. Or intercept the SDK's internal fetch to add Swoff's cache strategy headers

Configuration by Category

If you have cookie/session auth, skip withAuthHeaders() entirely:

function withAuthHeaders(headers, auth) {
  // Cookie/Session auth — nothing needed.
  // Browser auto-sends cookies on every same-origin request.
  return headers;
}

Note: Some cookie-based backends require a CSRF token for unsafe methods (POST, PUT, DELETE):

  • Laravel Sanctum: X-XSRF-TOKEN header from XSRF-TOKEN cookie
  • Django: X-CSRFToken header from csrftoken cookie

If your backend requires CSRF, add it:

function withAuthHeaders(headers, auth) {
  // Read CSRF token from cookie and set as header
  const csrfToken = getCookie("XSRF-TOKEN"); // or "csrftoken"
  if (csrfToken && !headers.has("X-XSRF-TOKEN")) {
    headers.set("X-XSRF-TOKEN", csrfToken);
  }
  return headers;
}

Bearer Token Auth — Header Setup

function withAuthHeaders(headers, auth) {
  if (auth?.token) {
    headers.set("Authorization", `Bearer ${auth.token}`);
  }
  return headers;
}

Custom Header Auth — Header Setup

// Authorization: Token (DRF TokenAuthentication)
function withAuthHeaders(headers, auth) {
  if (auth?.token) {
    headers.set("Authorization", `Token ${auth.token}`);
  }
  return headers;
}

// X-API-Key (API key auth)
function withAuthHeaders(headers, auth) {
  if (auth?.token) {
    headers.set("X-API-Key", auth.token);
  }
  return headers;
}

SDK-Managed Auth — Use the SDK Client

If you're using an SDK that auto-injects headers (Supabase JS client, Better-Auth client, Clerk components), you have two options:

Option A: Use authenticatedFetch with the token extracted from the SDK:

// Supabase example
import { supabase } from "./supabase-client.js";
import { setAuth } from "./swoff/auth-store.js";

async function loginWithSupabase(email, password) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });
  if (error) throw error;

  // Extract session and store in Swoff's auth store
  const session = data.session;
  await setAuth({
    token: session.access_token,
    user: session.user,
    expiresAt: Date.now() + session.expires_in * 1000,
  });

  // Now use authenticatedFetch for all subsequent calls
  // withAuthHeaders will inject: Authorization: Bearer <access_token>
}

// Then in withAuthHeaders:
function withAuthHeaders(headers, auth) {
  if (auth?.token) {
    headers.set("Authorization", `Bearer ${auth.token}`);
  }
  return headers;
}

Option B: Use the SDK's built-in client for data fetching:

// If your SDK auto-injects headers, use it directly
const { data: notes } = await supabase.from("notes").select("*");
// Auth is handled by the SDK internally

Tradeoff: Option A gives you Swoff's caching and offline queue. Option B gives you automatic auth but bypasses Swoff's cache layer.

Summary

Your Auth TypewithAuthHeaders()authenticatedFetch Needed?
Cookie / SessionNo-op (skip)Yes (for 401 handling + user cache)
Bearer TokenSet Authorization: BearerYes
Custom HeaderSet your headerYes
SDK-managedExtract token → set as BearerOptional (gives you caching)

Next Steps

On this page