Supabase auth provider
@sometic/auth-supabase wraps an injected Supabase Auth-like client. @supabase/supabase-js is an optional peer of the adapter. @sometic/auth core has no Supabase dependency and no Supabase lock-in.
When to use
- App already uses Supabase Auth (password and optional OAuth)
- You want Sometic orchestration + HTTP refresh on top of Supabase sessions
When not to use
- First-party REST without Supabase → Local
- Generic IdP without Supabase → OIDC
- Expecting Sometic to replace Supabase RLS (it cannot)
Installation
pnpm add @sometic/auth @sometic/auth-supabase @supabase/supabase-jsnpm install @sometic/auth @sometic/auth-supabase @supabase/supabase-jsyarn add @sometic/auth @sometic/auth-supabase @supabase/supabase-jsbun add @sometic/auth @sometic/auth-supabase @supabase/supabase-jsPeer: @supabase/supabase-js ^2 (optional peer metadata; install in the consuming app).
Usage
import { createClient } from "@supabase/supabase-js";
import { createAuth, createSessionStorageAuthStorage } from "@sometic/auth";
import { createSupabaseAuthProvider } from "@sometic/auth-supabase";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const auth = createAuth({
provider: createSupabaseAuthProvider({
auth: supabase.auth,
}),
storage: createSessionStorageAuthStorage(),
autoRefresh: true,
});
await auth.signIn({ email: "user@example.com", password: "secret" });import { createClient } from "@supabase/supabase-js";
import { createAuth, createSessionStorageAuthStorage } from "@sometic/auth";
import { createSupabaseAuthProvider } from "@sometic/auth-supabase";
import type { AuthController } from "@sometic/auth";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const auth: AuthController = createAuth({
provider: createSupabaseAuthProvider({
auth: supabase.auth,
}),
storage: createSessionStorageAuthStorage(),
autoRefresh: true,
});
await auth.signIn({ email: "user@example.com", password: "secret" });import { createClient } from "@supabase/supabase-js";
import { createAuth, createSessionStorageAuthStorage } from "@sometic/auth";
import { createSupabaseAuthProvider } from "@sometic/auth-supabase";
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const auth = createAuth({
provider: createSupabaseAuthProvider({
auth: supabase.auth,
}),
storage: createSessionStorageAuthStorage(),
autoRefresh: true,
});
await auth.signIn({ email: "user@example.com", password: "secret" });Pass supabase.auth (or any SupabaseAuthLike mock) into the adapter. Sometic does not construct the Supabase client.
Options
type SupabaseAuthProviderOptions = {
auth: SupabaseAuthLike;
mapUser?: (user: SupabaseUserLike) => AuthUser;
};Default map uses user.id, email, and user_metadata.displayName or full_name.
Capabilities
| Capability | Supported |
|---|---|
| signIn / signOut / register | ✓ |
| getSession / refresh / getUser | ✓ |
| passwordReset | ✓ |
| oauth | ✓ when signInWithOAuth exists on the injected client |
| emailVerification / mfa | not wrapped |
if (auth.supports("oauth")) {
await auth.startOAuth({ provider: "github", redirectUri: "https://app.example.com/callback" });
}Session mapping
Supabase access_token / refresh_token / expires_at (seconds) map into Sometic AuthTokens. expires_at values below 1e12 are treated as seconds and converted to ms.
Patterns
OAuth
Ensure the injected client exposes signInWithOAuth. Capability oauth is added only then. Complete the redirect with your app router; use Sometic session subscribe for UX.
HTTP API calls
import { createHttp } from "@sometic/http";
import { createAuthInterceptor } from "@sometic/http/auth";
const http = createHttp({
baseUrl: "https://api.example.com",
interceptors: [createAuthInterceptor({ auth })],
});Supabase RLS still enforces database access. Sometic HTTP refresh only coordinates bearer/session UX for your own APIs.
Limitations
- OAuth only when
signInWithOAuthis present - MFA not wrapped
- Anon key remains a public client key; never ship service role keys in the browser
- Client adapter does not secure APIs; RLS and Edge Functions do
FAQ
Does @sometic/auth depend on Supabase?
No. Only the optional adapter peers on @supabase/supabase-js.
Why inject supabase.auth instead of URL/key?
Keeps the adapter SDK-version flexible and testable with mocks. You control client options (storage, persistSession, etc.).