ADR-019: Authentication & Session Management

Status: Planning Owner: @bilal @deen Date: 2026-02-15

Why This Needs an ADR

There’s ambiguity in the codebase. The CI/CD doc references both NEXTAUTH_SECRET/NEXTAUTH_URL and Supabase Auth. Three distinct auth flows exist across the product but no single doc explains how they relate:

  1. Landlord dashboard — Supabase Auth (email/password, possibly magic link)
  2. Tenant chat — Supabase Phone/Email OTP (ADR-016 Tenant Chat App)
  3. Vendor acceptance — Stateless token links (ADR-006 Vendor Acceptance Tokens)

Questions to Resolve

1. Supabase Auth vs NextAuth — which and why?

The .env.example has both NEXTAUTH_SECRET and SUPABASE_ANON_KEY. Are both in use? Options:

ApproachProsCons
Supabase Auth onlySingle auth system, RLS works natively, OTP built-inLess flexibility for custom session claims
NextAuth wrapping SupabaseCustom session handling, familiar APITwo systems to maintain, RLS bypass risk
NextAuth replaced by Supabase middlewareSimpler stack, native Supabase ecosystemMigration effort if NextAuth is already used

Likely answer: Supabase Auth only. NextAuth references may be legacy from early setup. Need to verify in codebase.

2. JWT Claims Structure

For multi-org support (ADR-001 Multi-Tenancy Access Model), the session needs to carry:

// What needs to be in the JWT/session
{
  userId: string
  email: string
  organisationId: string        // Currently selected org
  organisationIds: string[]     // All accessible orgs
  role: 'owner' | 'admin' | 'staff' | 'envo_support'
}

How does organisationId get into the session?

  • Option A: Custom JWT claims via Supabase Auth hooks
  • Option B: App-level session enrichment after Supabase auth
  • Option C: Supabase app.current_org_id session variable for RLS

3. Multi-Org Session Switching

When a user has access to multiple orgs:

  • How do they switch? (Dropdown in header? Separate login?)
  • Does switching invalidate the current session?
  • How does the GraphQL context get the current org?

4. Auth Middleware Architecture

Request → Supabase Auth check → Extract user → Load org access → Set context
                                                                     ↓
                                                              GraphQL resolvers
                                                              use ctx.organisationId

What middleware pattern? Next.js middleware? Per-route handler? Pothos auth plugin?

5. Tenant Auth (Chat App)

  • Supabase Phone OTP — does this create a users record or a separate auth identity?
  • How does the OTP session map to a tenants record?
  • Is the tenant auth session completely separate from the dashboard auth?

6. Dev Auth

  • ADR-016 mentions cookie-based tenant impersonation for dev
  • Does the dashboard have similar dev shortcuts?
  • How do seed users (Infrastructure) relate to auth?

Depends On

  • Current codebase state (is NextAuth actually used?)
  • Supabase Auth capabilities (custom claims, hooks)