Authentication ​

@sometic/auth is provider-independent session orchestration: sign-in and sign-out, hydrate, refresh coordination, storage adapters, capability-gated flows, and UX-only authorization helpers. It does not embed Firebase, Supabase, or OIDC SDKs.

System standout: session epoch

Every AuthSession carries an additive epoch number. Sign-in, sign-out, and user-id change bump it. Wire createAppShell (or bindQueryToAuth / bindAuthToHttp) so privileged query cache and cross-epoch HTTP replays cannot survive a user switch. Use auth.getEpoch(), requestStepUp / completeStepUp for MFA/reauth UX.

Production backends plug in through optional packages: @sometic/auth-local, @sometic/auth-firebase, @sometic/auth-supabase, @sometic/auth-oidc. Provider SDKs are optional peers on those adapters. Core never locks you into a cloud vendor.

Overview ​

ConcernPackage / subpath
Orchestration@sometic/auth → createAuth
Provider contract@sometic/auth/provider
Session helpers@sometic/auth/session
Storage@sometic/auth/storage
Refresh single-flight@sometic/auth/refresh
UX policies@sometic/auth/authorization
Deterministic tests@sometic/auth/test-provider
HTTP 401 seam@sometic/http/auth → createAuthInterceptor

When to use ​

  • One auth behavior model across Vanilla, React, Vue, and Elements
  • Swappable providers without rewriting UI session code
  • Coordinated refresh and multi-tab logout without reinventing race handling

When not to use ​

  • Full-stack Auth.js / NextAuth as your only stack (different product shape)
  • Expecting client can() to secure APIs (it cannot)
  • Shipping login form UI from this package (auth is headless)

Capability matrix ​

Providers declare a Set<AuthCapability>. Unsupported flows throw AUTH_UNSUPPORTED.

Capabilitytestlocalfirebasesupabaseoidc
signIn✓✓✓✓
signOut✓✓✓✓✓
register✓✓✓✓
getSession✓✓✓✓✓
refresh✓✓✓✓✓
getUser✓✓✓✓✓
passwordReset✓✓✓✓
emailVerification✓✓
oauth✓*✓
mfa

* Supabase OAuth when signInWithOAuth is present on the injected client.

Quick start ​

js
import { createAuth, createMemoryAuthStorage, createTestAuthProvider } from "@sometic/auth";

const auth = createAuth({
    provider: createTestAuthProvider(),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();
await auth.signIn({ email: "demo@example.com", password: "password" });
auth.isAuthenticated();
auth.dispose();
ts
import { createAuth, createMemoryAuthStorage, createTestAuthProvider } from "@sometic/auth";
import type { AuthController } from "@sometic/auth";

const auth: AuthController = createAuth({
    provider: createTestAuthProvider(),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();
await auth.signIn({ email: "demo@example.com", password: "password" });
auth.isAuthenticated(); // true
auth.dispose();
js
import { createAuth, createMemoryAuthStorage, createTestAuthProvider } from "@sometic/auth";

const auth = createAuth({
    provider: createTestAuthProvider(),
    storage: createMemoryAuthStorage(),
});

await auth.whenReady();
await auth.signIn({ email: "demo@example.com", password: "password" });
auth.isAuthenticated();
auth.dispose();
html
<script src="https://cdn.jsdelivr.net/npm/@sometic/auth@1.2.3/dist/cdn/sometic-auth.iife.js"></script>
<script>
    const auth = SometicAuth.createAuth({ provider });
    auth.signIn({ email: "a@b.c", password: "secret" });
</script>
html
<script type="module">
    import { createAuth } from "https://cdn.jsdelivr.net/npm/@sometic/auth@1.2.3/dist/cdn/sometic-auth.esm.js";

    const auth = createAuth({ provider });
    await auth.signIn({ email: "a@b.c", password: "secret" });
</script>

Swap the provider for production:

ts
import { createLocalAuthProvider } from "@sometic/auth-local";

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

Security boundary ​

Browser auth state is untrusted. Attackers who can run script can read non-httpOnly tokens, ignore can(), and trigger refresh. Mitigations live on the server: httpOnly cookies where possible, short-lived access tokens, CSRF, refresh rotation, and server authorization.

See Authorization and Troubleshooting.

Docs in this section ​

PageTopic
InstallationCore + optional provider peers
ConfigurationcreateAuth options, storage, cross-tab
Local providerREST JSON backend
FirebaseInjected Firebase Auth adapter
SupabaseInjected Supabase Auth adapter
OIDCAuthorization Code + PKCE
Session managementStatus, hydrate, events
Token refreshSingle-flight, auto-refresh
InterceptorsHTTP auth refresh queue
AuthorizationUX-only can / policies
TroubleshootingCommon failure modes

Framework adapters ​

SurfacePackage
React@sometic/react/auth (AuthProvider, useAuth, useSession, useCan)
Vue@sometic/vue/auth
Elements@sometic/elements/auth (sometic-auth-status)

FAQ ​

Why are Firebase / Supabase / OIDC not in @sometic/auth? ​

Auth core stays provider-agnostic. SDKs live in optional @sometic/auth-* packages so apps only install what they use.

Is client authorization secure? ​

No. can(), authorize(), and route guards only affect UX. APIs must enforce authorization on the server.

How does this compare to using a provider SDK alone? ​

Provider SDKs alone couple UI to one vendor and leave refresh races, multi-tab logout, and capability gates to you. Sometic owns orchestration; adapters wrap SDKs you already inject.

Where do tokens live? ​

Wherever the storage adapter writes them. Memory is SSR-safe and lost on reload. sessionStorage is tab-scoped. localStorage is durable and highest XSS exposure for bearer tokens. Prefer httpOnly cookies when your backend supports them.

What is session epoch? ​

AuthSession.epoch increments on sign-in, sign-out, and user-id change (not on token refresh). auth.getEpoch() reads it. createAppShell clears query cache and refuses stale HTTP replays when the epoch bumps.