Skip to content

ADR-0021: Clerk Organization Model — Shared App Now, Separate Apps Later

Status: Accepted Date: 2026-04-24 Session: 70

Context

Curaway has 7 actor types (patients, providers, coordinators, MSO doctors, facilitators, platform admins, super admins) accessing 5 different portal apps. Each needs authentication and tenant-scoped authorization.

Clerk supports two models: - One Clerk app + Organizations — all users in one pool, grouped by org with roles - Separate Clerk apps — one app per portal, fully isolated user pools

Decision

Phase 1 (Now): One Clerk App + Organizations

Use a single Clerk application with Clerk Organizations for multi-tenancy.

Curaway Concept Clerk Concept
Hospital/clinic tenant Clerk Organization
Actor type (provider/coordinator/MSO) Organization role
Patient User (no org, personal context)
Super admin (SD) User in multiple orgs with admin role
New hospital onboarding Create new Clerk Organization via API

Org roles defined in Clerk: - org:provider:admin — hospital admin, manages staff - org:provider:staff — hospital staff, views cases - org:coordinator — Curaway ops staff - org:mso:doctor — second-opinion doctor - org:facilitator — external agent - org:admin — platform admin

Why now: - Single Clerk dashboard for all user management - Users can belong to multiple orgs (SD is coordinator + admin) - One publishable key shared across all portal apps - No additional Clerk cost (orgs included in plan) - Fastest path to working auth across all portals - Clerk's <OrganizationSwitcher> component handles multi-org UX

Phase 2 (Later): Separate Clerk Apps Per Stakeholder

When the platform scales and requires full isolation, migrate to separate Clerk apps:

Portal Clerk App Domain When
Patient Clerk App 1 app.curaway.ai Current
Provider Clerk App 2 provider.curaway.ai Post-Series A
Coordinator Clerk App 3 ops.curaway.ai Post-Series A
MSO Clerk App 4 mso.curaway.ai Post-Series A
Admin Clerk App 5 admin.curaway.ai Post-Series A

Why later: - Full isolation: provider users physically cannot access coordinator auth - Separate branding per portal (different logos, colors, SSO providers) - Separate rate limits and security policies - Independent scaling and billing - Compliance: some providers may require their own identity provider

Migration path: 1. Export users from shared Clerk app 2. Create per-portal Clerk apps 3. Import users to appropriate apps 4. Update env vars per portal deployment 5. Backend maps clerk_app_id + clerk_user_id → Curaway user (instead of just clerk_user_id)

Trigger for Phase 2: - Provider requests SSO integration with their own IdP - Compliance requirement for isolated identity stores - Scale beyond 50 organizations in single Clerk app - Different auth requirements per portal (e.g., MFA mandatory for MSO, optional for facilitators)

Consequences

Positive (Phase 1): - Single point of user management - Fast implementation (1 publishable key for all portals) - SD can switch between orgs without separate logins - Clerk handles role management, org invites, member lists

Negative (Phase 1): - All portals share auth boundary — a provider could visit coordinator URL (RBAC blocks access, but the Clerk sign-in page is shared) - If Clerk app is compromised, all portals are affected - Clerk org limits may apply at high scale

Positive (Phase 2 migration): - Clean separation is possible because backend already uses tenant_id + clerk_org_id — the mapping layer is abstracted - Patient app already runs its own Clerk app — the pattern exists

Risk: - Phase 2 migration requires downtime or dual-auth period - User accounts need re-creation or import in new Clerk apps - Must maintain backward compatibility in backend JWT validation

References

  • ADR-0018: Multi-tenancy platform architecture
  • Clerk Organizations docs: https://clerk.com/docs/organizations
  • Current patient-app Clerk setup: apps/patient-app/src/App.tsx
  • RBAC middleware: app/middleware/rbac_middleware.py