Prood
ApplicationsStorefront

Auth & Tenant Resolution

Customer authentication and host-based tenant resolution in the storefront.

The storefront handles two cross-cutting concerns: customer authentication (Better Auth) and tenant resolution (which merchant store to display).

Customer authentication

Setup

Better Auth is configured in lib/auth/server.ts:

SettingValue
ProviderEmail/password only
DatabaseNeon via Drizzle (lib/auth/db.ts, lib/auth/schema.ts)
Tablesuser, session, account, verification
Organization pluginNot enabled — buyers are not org members

Auth seam

lib/auth/index.ts exposes getSession() and getCurrentUser() as thin Better Auth wrappers for pages and server actions.

Commerce identity

Logged-in buyers map to customers.id (internal UUID) via customers.auth_user_id. Orders and carts never store Better Auth user.id or email. See Privacy & data.

On sign-up, ensure a commerce customer row exists for the tenant (ensureCustomerForAuthUser).

Protected routes

Account pages check for an active session:

const session = await getSession()
if (!session) redirect('/login')

The API resolves the session → customers.id and filters orders server-side.

API routes

Better Auth handler at /api/auth/[...all] handles sign-up, sign-in, sign-out, and session cookies.

Tenant resolution

How it works

lib/tenant.tsresolveTenantId():

  1. Custom domain lookup (tenant_domain, verified)
  2. Subdomain {slug}.{NEXT_PUBLIC_PLATFORM_DOMAIN} → organization by slug
  3. DEFAULT_TENANT_ORG_ID (local dev only)
  4. Otherwise notFound() in production

There is no hardcoded org_demo fallback — set DEFAULT_TENANT_ORG_ID explicitly after pnpm db:setup seed.

Resolution priority

PrioritySourceExample
1Custom domain (verified)shop.merchant.com
2Platform subdomainmerchant.prood.app
3DEFAULT_TENANT_ORG_IDLocal dev only
4Not found404

Database lookup

lookupTenantByHost() from @prood/platform (re-exported in lib/tenant-db.ts).

Forwarding to API

The storefront API client forwards the Host header and session cookie to the Commerce API for tenant-scoped routes.

Development setup

  1. Run pnpm db:setup and note the seeded organization id
  2. Set DEFAULT_TENANT_ORG_ID=<seeded-org-id> in .env.local
  3. Access http://localhost:3000

For subdomain testing, add /etc/hosts entries and set NEXT_PUBLIC_PLATFORM_DOMAIN.

On this page