---
title: "Auth pattern — Supabase Auth + RLS"
url: https://mdfy.app/mjpt9oz8
updated: 2026-05-16T14:17:17.590Z
source: "demo-seed"
---
# Auth pattern — Supabase Auth + RLS

## Why Supabase Auth (not Clerk / NextAuth / Auth0)

- Same vendor as DB → JWT `sub` ↔ `auth.users.id` join is native (no email-lookup roundtrip)
- RLS lives next to the data — auth and authz are one table read away
- Magic link + Google + GitHub out of the box
- Free up to 50k MAU; matches our pre-revenue runway

## RLS policies (per role)

```sql
-- Tenant isolation: every table has user_id or organization_id
CREATE POLICY "own rows only" ON pages
  FOR SELECT USING (
    organization_id IN (
      SELECT organization_id FROM organization_members
      WHERE user_id = auth.uid()
    )
  );
```

Editors get write access. Viewers get read-only. Admin role bypasses
via service-role key on the server only — never in the browser.

## Session handling

- Server components → `createServerClient` with cookie store
- Client components → `@supabase/ssr` browser client
- API routes → verify the JWT with `verifyAuthToken` (see `lib/verify-auth.ts`)
- Anonymous edits → cookie-grouped `anonymous_id` until first sign-in

## Magic link tips

- Email template lives in Supabase dashboard, not in code (annoying for PRs)
- 24h link TTL; user pasting into wrong browser → friction. Use OTP fallback when telemetry shows >5% magic-link bounces.

## Open questions
- Org invitations: do we want self-serve invite links or just email invitations?
- Should we move to passkeys before launch or after?
