fayzfayz sdk
PTEN

Auth — overview

Authentication in Fayz is abstracted behind a contract: the AuthAdapter. The same useAuth works whether you're running against Supabase in production or against a mock adapter in previews. The auth backend is a detail you swap — the code that consumes auth doesn't change.

The contract: AuthAdapter

An adapter implements the session operations — getSession, signIn, signUp, signOut, signInWithOAuth, resetPassword, onAuthStateChange and friends. @fayz-ai/auth ships two out of the box:

  • createSupabaseAuthAdapter({ supabaseUrl, supabaseAnonKey }) — real auth via Supabase Auth. Maps the Supabase user and session to the Fayz types (AuthUser, AuthSession).
  • createMockAuthAdapter() — a simulated user persisted in localStorage, for development and previews without a backend. It comes already "signed in" as a demo user.

Switching from one to the other is switching the factory — nothing else.

Consuming it: AuthProvider + useAuth

In a code-first app, you mount AuthProvider at the root, passing the adapter you chose, and read the state with the useAuth hook:

import { AuthProvider, useAuth, createSupabaseAuthAdapter } from '@fayz-ai/auth'

const adapter = createSupabaseAuthAdapter({
  supabaseUrl: import.meta.env.VITE_SUPABASE_URL,
  supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
})

function Root() {
  return (
    <AuthProvider adapter={adapter}>
      <App />
    </AuthProvider>
  )
}
function UserBadge() {
  const { user, isAuthenticated, signOut } = useAuth()
  if (!isAuthenticated) return <span>Guest</span>
  return <button onClick={signOut}>{user?.fullName}</button>
}

useAuth exposes user, session, isLoading, isAuthenticated, error and the actions (signIn, signUp, signOut, signInWithOAuth, resetPassword, …). Calling it outside an <AuthProvider> throws — on purpose.

How this works in the manifest-first scaffold

Here's the honest part. In the app generated by fayz create — the manifest-first path — you don't mount AuthProvider by hand. The platform runtime reads backend.provider from the manifest and wires the matching adapter for you: mock uses the simulated adapter; supabase uses the real one with the credentials from your .env.local. Auth "follows" the backend you declare — switching provider in the manifest also switches the auth adapter.

The explicit AuthProvider / useAuth you saw above are the code-first path: apps that mount their own React tree (via defineSaas or headless) and want direct control over where the session enters. Both paths use the same AuthAdapter contract — only who mounts the provider changes.

In short: manifest-first → the auth adapter follows backend.provider, no code. Code-first / headless → you mount AuthProvider with the adapter of your choice. Identical contract either way.

Auth and multi-tenancy

The session resolved by auth is what feeds the public.user_tenant_ids() function in the database — meaning auth is what ultimately decides which rows RLS lets you see. Auth and isolation go together; see RLS and multi-tenancy.


Next: build your first capability in Incubator.