Auth configuration

createAuth wires a capability-aware AuthProvider, optional storage, refresh behavior, and cross-tab messaging into a single AuthController.

Signature

ts
function createAuth(options: CreateAuthOptions): AuthController;

type CreateAuthOptions = {
    provider: AuthProvider;
    storage?: AuthStorage;
    skewMs?: number;
    crossTab?: AuthCrossTabBus | false;
    now?: () => number;
    autoRefresh?: boolean;
    refreshIntervalMs?: number;
    environment?: AuthEnvironment | false;
};

Minimal setup

js
import { createAuth, createMemoryAuthStorage } from "@sometic/auth";
import { createLocalAuthProvider } from "@sometic/auth-local";

const auth = createAuth({
    provider: createLocalAuthProvider({ baseUrl: "https://api.example.com" }),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();
ts
import { createAuth, createMemoryAuthStorage } from "@sometic/auth";
import { createLocalAuthProvider } from "@sometic/auth-local";
import type { AuthController } from "@sometic/auth";

const auth: AuthController = createAuth({
    provider: createLocalAuthProvider({ baseUrl: "https://api.example.com" }),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();
js
import { createAuth, createMemoryAuthStorage } from "@sometic/auth";
import { createLocalAuthProvider } from "@sometic/auth-local";

const auth = createAuth({
    provider: createLocalAuthProvider({ baseUrl: "https://api.example.com" }),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();

Default storage is memory when omitted. Default skew accounts for clock drift near expiry. Cross-tab bus defaults to a BroadcastChannel-backed bus when available; pass false to disable.

Options reference

OptionDefaultPurpose
providerrequiredCapability-aware auth backend
storagememoryPersist / restore session snapshots
skewMsimplementation defaultTreat session expired slightly early
crossTabbroadcast when availableMulti-tab logout / session messages
nowDate.nowInjectable clock for tests
autoRefreshoff unless trueProactive refresh near expiry
refreshIntervalMsimplementation defaultPolling interval when auto-refresh on
environmentglobalThis-likeVisibility / online hooks; false disables

Storage adapters

FactoryLifetimeXSS note
createMemoryAuthStorage()Process / tab JS heapSafest for bearer tokens; lost on reload
createSessionStorageAuthStorage()Tab sessionStorageSurvives reload in-tab; script can read
createLocalStorageAuthStorage()Origin localStorageDurable; highest XSS exposure for tokens
createCustomAuthStorage({ get, set, remove })Your choicePrefer httpOnly cookie bridges on the server
ts
import { createAuth, createSessionStorageAuthStorage, createBroadcastAuthBus } from "@sometic/auth";

const auth = createAuth({
    provider,
    storage: createSessionStorageAuthStorage({ key: "sometic.auth.session" }),
    crossTab: createBroadcastAuthBus("sometic-auth"),
    autoRefresh: true,
});

Prefer httpOnly cookies when your API supports them. Client storage adapters then hold UX session metadata, not long-lived refresh secrets, when your architecture allows.

Cross-tab

FactoryBehavior
createBroadcastAuthBus(channelName?)BroadcastChannel messages { type: "logout" } / { type: "session" }
createNoopAuthBus()No peer traffic
falseDisable cross-tab entirely

Sign-out posts logout so peer tabs clear session without starting a refresh storm. Do not invent a second BroadcastChannel for the same app identity.

Environment hooks

Pass a custom environment in tests or SSR shells. Pass false to skip visibility and online listeners (useful for Node unit tests).

ts
const auth = createAuth({
    provider: createTestAuthProvider(),
    environment: false,
    now: () => frozenClock,
});

Controller surface (summary)

MethodRole
whenReady / ready / isReadyHydration settled
getSession / getUser / tokensSnapshot accessors
subscribe / onSession and named events
supports(capability)Capability probe
signIn / signOut / registerCore flows
refresh / ensureFreshSession / handleUnauthorizedToken freshness
hydrateInject a known session
OAuth / MFA / password / verify / revokeCapability-gated
can / cannot / authorize / assertAuthorizedUX policies
disposeClear flights, listeners, timers

Patterns

App bootstrap

ts
export const auth = createAuth({
    provider: createLocalAuthProvider({ baseUrl: import.meta.env.VITE_API_URL }),
    storage: createSessionStorageAuthStorage(),
    autoRefresh: true,
});

export async function bootstrapAuth() {
    await auth.whenReady();
    return auth.getSession();
}

Disable auto-refresh; refresh only on 401

ts
const auth = createAuth({
    provider,
    autoRefresh: false,
});
// Wire createAuthInterceptor so HTTP calls auth.handleUnauthorized()

Edge cases

  • Calling methods after dispose() throws AUTH_DISPOSED.
  • Unsupported capabilities throw AUTH_UNSUPPORTED before the provider method runs.
  • SSR: never touch window / storage at import time; construct auth inside app bootstrap with memory storage or an injected custom store.

FAQ

Should autoRefresh always be on?

Not always. Short-lived SPA sessions often refresh on 401 via HTTP. Background refresh helps long-lived tabs; it needs a working refresh capability and careful multi-tab coordination.

Can I pass raw Firebase into createAuth?

No. Wrap with createFirebaseAuthProvider({ auth: firebaseAuth }) from @sometic/auth-firebase. Core only accepts AuthProvider.