Skip to main content
ORbit uses Supabase Auth for authentication with middleware-based session validation and row-level security (RLS) on every database table.
Authentication is shared across both the web and iOS platforms. Both connect to the same Supabase Auth instance with the same user accounts and the same RLS policies.

Auth flow

User → Login (email/password) → Supabase Auth → Session token
    → Cookies (web) / Keychain (iOS) → RLS enforces access

Middleware

The Next.js middleware (middleware.ts) runs on every request and enforces authentication on protected routes.

Public routes (no auth required)

RoutePurpose
/auth/*Auth callbacks, password reset
/invite/*Device rep signup portal
/loginLogin page
/status/*Public patient status pages (escort links)

Public API routes

RoutePurpose
/api/invite/acceptAccept device rep invitation
/api/create-device-repCreate device rep account
/api/check-auth-statusCheck if current session is valid

Protected routes

All other routes require a valid session:
  • Page routes → redirect to /login if not authenticated
  • API routes → return 401 JSON response

Supabase client creation

Browser (client components)

import { createClient } from '@/lib/supabase'
const supabase = createClient()

Server (server components, route handlers)

import { createClient } from '@/lib/supabase-server'
const supabase = await createClient()

Access levels

LevelScopeTypical user
global_adminAll facilities, all dataPlatform owner
facility_adminOwn facility onlyOR manager, facility director
surgeonOwn cases + facility scorecardsSurgeon
device_repAssigned cases onlyImplant sales rep

Row-level security

Every table has RLS policies enforcing facility scoping:
  • Users see only data within their assigned facility
  • Global admins bypass facility filters via is_global_admin()
  • Device reps have restricted policies limiting access to assigned cases

Session management

  • Web: Sessions in HTTP-only cookies, refreshed automatically
  • iOS: Tokens in Keychain (migrated from UserDefaults)
  • Expiration: Based on Supabase project settings with automatic refresh

Helper functions

FunctionDescription
get_current_user_facility()Returns facility_id for the authenticated user
get_my_access_level()Returns access_level for the current user
is_facility_accessible(facility_id)Checks if user can access a specific facility
is_global_admin()Returns true if user has global admin privileges
These helper functions are used extensively in RLS policies. When writing new RLS policies, use get_current_user_facility() to scope access and is_global_admin() to allow cross-facility access for admins.

Security considerations

RLS enforces data access at the database level, which means even if application code has a bug, users can’t access data outside their facility. This is defense-in-depth — the application also filters by facility_id, but RLS is the ultimate safety net.
Device reps have specialized RLS policies that only allow access to cases they’re assigned to (via case_device_companies). They can’t see the full case list or other facility data.
On the web, sessions are automatically refreshed via HTTP-only cookies. If refresh fails, the user is redirected to /login. On iOS, Keychain-stored tokens are refreshed automatically by the Supabase Swift SDK.

Next steps