# Auth and store resolution (https://prood.com/docs/apps/storefront/auth-tenant)

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

## Customer authentication [#customer-authentication]

### Setup [#setup]

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

| Setting | Value |
| --- | --- |
| Provider | Email/password only |
| Database | Neon via Drizzle (`lib/auth/db.ts`, `lib/auth/schema.ts`) |
| Tables | `user`, `session`, `account`, `verification` |
| Organization plugin | **Not enabled** — buyers are not org members |

### Auth seam [#auth-seam]

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

### Commerce identity [#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](/docs/architecture/privacy-data).

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

### Protected routes [#protected-routes]

Account pages check for an active session:

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

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

### API routes [#api-routes]

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

## Tenant resolution [#tenant-resolution]

### How it works [#how-it-works]

`lib/tenant.ts` → `resolveTenantId()` (pages, from the `[host]` root param) and `resolveRequestTenantId()` (Route Handlers and Server Actions, from the `Host` header). Both use `tenantIdForHost()`, cached per host for five minutes:

1. Custom domain lookup (`tenant_domain`, verified)
2. Subdomain `{slug}.{NEXT_PUBLIC_PLATFORM_DOMAIN}` → organization by slug
3. Otherwise redirect to the marketing site (`NEXT_PUBLIC_WEB_URL`)

There is no fallback tenant.

### Resolution priority [#resolution-priority]

| Priority | Source | Example |
| --- | --- | --- |
| 1 | Custom domain (verified) | `shop.example.com` |
| 2 | Platform subdomain | `merchant.prood.app` |
| 3 | Unknown host | Redirect to marketing site |

### Database lookup [#database-lookup]

`lookupTenantByHost()` from `@prood/platform`.

### Forwarding to API [#forwarding-to-api]

The storefront API client forwards `x-storefront-host` and the session cookie to the Commerce API for tenant-scoped routes.

## Development setup [#development-setup]

1. Run `pnpm db:migrate` and `pnpm db:seed`
2. With `NEXT_PUBLIC_PLATFORM_DOMAIN=localhost`, open [http://prood-demo.localhost:3000](http://prood-demo.localhost:3000) (the seeded `org_demo` store, slug `prood-demo`)

## Related pages [#related-pages]

<Cards>
  <Card title="Privacy & data" href="/docs/architecture/privacy-data" description="UUID identity and PII boundaries." />
  <Card title="Multi-tenant platform" href="/docs/architecture/multi-tenant" description="RLS, organizations, and isolation." />
  <Card title="Dashboard domains" href="/docs/apps/dashboard/domains" description="Custom domain setup." />
</Cards>
