01 — Auth & Multi-Tenant Architecture
Ring: 1 (Launch Blocker) Dependency: None — everything else depends on this Handbook: Ch. 39 (multi-tenant model), Ch. 48 (security), Ch. 141 (organization_users), Ch. 166 (RLS)
Problem
- No login exists. We don’t know who the user is.
ORGANIZATION_IDis hardcoded everywhere.- RLS policies are temporary (
temp_dev_anon_access— hardcoded to DOSE org_id). - Billing, credit, multi-user — all impossible without auth.
- System behaves like an “internal tool” but it is now a SaaS product.
Decisions
D1: Login Methods
- Email/password
- Google OAuth
- Apple OAuth
D2: Role System — 2 Layers
Platform Roles (internal, not visible to customers):| Role | Description |
|---|---|
super_admin | Founder. View all orgs/data, manage platform. JWT raw_app_meta_data.platform_role |
| Role | Permissions |
|---|---|
owner | Everything + billing + delete org + ownership transfer. 1 person per org. |
admin | Invite/remove members, settings, all features. Cannot delete org. |
member | Discovery, headhunt, lead management — all features within plan limits. |
viewer | Read-only — view companies/leads, no execution permissions. |
The same person can hold different roles in different orgs.
D3: Plan = Org Level
D4: Multi-Org Rules
| Plan | Org CREATION | Being INVITED to an Org |
|---|---|---|
| Free | 1 | Unlimited |
| Pro | 1 | Unlimited |
| Team | 1 | Unlimited |
| Enterprise | Unlimited | Unlimited |
D5: Signup & Invitation Flow
D6: DOSE Migration Scenario
Data Model
Current (will change)
Target
RLS Policies (JWT-based)
Session & Context
Current Code Impact
To Be Removed / Changed
| File | Change |
|---|---|
lib/constants.ts → ORGANIZATION_ID | Remove → session.activeOrgId |
lib/supabase.ts | Anon client → Auth client (with session token) |
| All API routes (15+) | ORGANIZATION_ID → from session |
| All client-side hooks | Auth context + org context to be added |
export_ai_profiles table | Remove or deprecate |
| 18 table RLS policies | temp_dev_anon_access → JWT-based |
validateApiKey.ts | Stays for external (batch script) use, JWT for UI |
New Files
| File | Content |
|---|---|
lib/auth/session.ts | getSession(), requireAuth(), requireRole() |
lib/auth/context.tsx | React AuthProvider + OrgProvider |
app/login/page.tsx | Login UI (email + Google + Apple) |
app/signup/page.tsx | Signup UI |
app/org/new/page.tsx | Org creation |
app/org/settings/page.tsx | Org settings + member management |
middleware.ts | Auth guard (protected routes) |
Future Decisions (not now, but not forgotten)
FD-1: Shared Companies (Ring 2)
Companies will become org-independent global data.export_ai_companies.organization_idwill be removed, replaced by anexport_ai_org_companiesjunction table. This is the SaaS data moat strategy — as the platform grows, AI cost per query decreases. Why not now: Large migration, all queries change. After auth is stable.
FD-2: Platform Support Role (Ring 3+)
A platform_support role will be added for the support team. Can view user data but cannot modify it. For debugging + customer support.
Why not now: Solo team, not needed yet.
FD-3: Enterprise Feature-Based Roles (Ring 3+)
In the Enterprise plan, roles will not just be owner/admin/member/viewer but can carry feature-based permissions. Example:can_run_batch,can_export_data,can_manage_billing,can_run_discovery. This way an Enterprise customer can fine-tune like “sales team can run discovery but not batch operations.” Requires a custom role builder UI. Why not now: 4 fixed roles are sufficient at launch. Needs will clarify when Enterprise customers arrive.
FD-4: Org Switching UI (Ring 2)
Org switcher in the sidebar for multi-org users. Switching the active org updates JWT claims. Why not now: Everyone has a single org initially. Needed when Enterprise plan launches.
Atomic Tasks (will become TODO items)
| # | Task | Estimated Size |
|---|---|---|
| AUTH-1 | Enable Supabase Auth (email + Google + Apple providers) | Small |
| AUTH-2 | Update export_ai_organizations table (plan, slug, stripe_customer_id, created_by, settings) | Migration |
| AUTH-3 | Create export_ai_organization_members table (role, status, invited_by) | Migration |
| AUTH-4 | export_ai_profiles → deprecate/remove | Migration |
| AUTH-5 | lib/auth/session.ts — getSession, requireAuth, requireRole | Medium |
| AUTH-6 | lib/auth/context.tsx — React AuthProvider + OrgProvider + useAuth hook | Medium |
| AUTH-7 | Login + Signup + Org creation pages | Medium |
| AUTH-8 | middleware.ts — auth guard (protected routes) | Small |
| AUTH-9 | Replace ORGANIZATION_ID → session.activeOrgId in all API routes | Large (15+ files) |
| AUTH-10 | Auth context integration in all client hooks | Large |
| AUTH-11 | Rewrite 18 table RLS policies → JWT-based + delete temp policies | Large |
| AUTH-12 | Create DOSE founder user + owner + super_admin set | Small |
| AUTH-13 | Remove validateApiKey.ts dev bypass (SEC-F) | Small |
| AUTH-14 | Org settings + member invite/management page | Medium |