Swoff Swoff

Current User

Fetch, cache, and invalidate the current user for offline access.

Current User

Prerequisites: Auth Store, Auth-Aware Fetch.

The CLI generates swoff/auth-user.js when features.auth.enabled is true. It uses a separate swoff-auth-user IndexedDB database (distinct from swoff-auth) to cache the current user profile for offline display.

Once authenticated, fetch and cache the current user so their data is available offline. On logout, clear everything.

User Store

swoff/auth-user.js
import { getAuth } from "./auth-store.js";

const DB_NAME = "swoff-auth-user";
const STORE_NAME = "user";

function openUserDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, 1);
    request.onupgradeneeded = (e) => {
      const db = e.target.result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: "key" });
      }
    };
    request.onsuccess = (e) => resolve(e.target.result);
    request.onerror = (e) => reject(e.target.error);
  });
}

Lifecycle Flow

User logs in (your login function)

setAuth({ token, user, expiresAt })

fetchCurrentUser() → stores in IndexedDB for offline access

[on any page load]
getAuth() → IndexedDB → memory → user is authenticated

getCachedUser() → shows user data immediately (no network needed)

[if online] authenticatedFetch("/api/me") → updates cached user

User logs out

clearAuth() + clearCachedUser()

CLEAR_RUNTIME_CACHE → SW invalidates user-specific cached responses

Full Login Flow

import { setAuth } from "./swoff/auth-store.js";
import { fetchCurrentUser } from "./swoff/auth-user.js";

async function onLogin(authResponse) {
  // 1. Store auth credentials
  await setAuth({
    token: authResponse.token,
    user: authResponse.user,
    expiresAt: authResponse.expiresAt,
  });

  // 2. Fetch and cache the full current user profile
  try {
    const user = await fetchCurrentUser();
    // user is now cached in IndexedDB
  } catch {
    // Offline? Use the minimal user from login response
    await cacheUser(authResponse.user);
  }

  // 3. Notify the app
  window.dispatchEvent(
    new CustomEvent("sw-auth-state-change", {
      detail: { authenticated: true },
    }),
  );
}

Full Logout Flow

import { clearAuth } from "./swoff/auth-store.js";
import { clearCachedUser } from "./swoff/auth-user.js";

async function onLogout() {
  await fetch("/api/logout", { method: "POST" }).catch(() => {});
  await clearAuth();
  await clearCachedUser();

  // Clear SW runtime cache (user-specific cached responses)
  if ("serviceWorker" in navigator) {
    const registration = await navigator.serviceWorker.getRegistration();
    if (registration?.active) {
      registration.active.postMessage({ type: "CLEAR_RUNTIME_CACHE" });
    }
  }

  window.dispatchEvent(
    new CustomEvent("sw-auth-state-change", {
      detail: { authenticated: false },
    }),
  );
}

Next Steps

On this page