Auth flow, route protection, and billing gating
Auth & Middleware
Surflink uses Supabase Auth with server-side session management via @supabase/ssr, enforced in src/middleware.js.
Auth flow
- Sign up / sign in via
/loginand/signup(coaches), or invite/club links (students). - Sessions are stored in cookies and refreshed by the middleware on each request.
- Profile creation. New coaches get a
coachesprofile and an auto-provisioned org (Owner) + trial subscription. - Role routing after login: coach ->
/dashboard, student ->/athlete/dashboard.
What the middleware does
- Creates an SSR Supabase client and refreshes the auth token (
getUser()). - Cleans up stale chunked auth cookies (prevents 431 "Header Too Large" errors).
- Redirects unauthenticated users on protected routes to
/login, and signed-in users away from/login//signupto/dashboard. - Sends users with an unverified email to
/verify-email. - Billing gating: for a non-super-admin coach whose org has no active subscription (
active/trialing/past_due), most routes redirect to/pricing; a bypass list keeps essential paths reachable. /api/video/streamis skipped entirely by the middleware (it's protected by HMAC tokens instead) to avoid 431 errors from range-request cookies.
Public routes (no auth)
/, /login, /signup, /pricing, /forgot-password, /reset-password, /verify-email, /auth/callback, /reports, /invite, /docs, /p, /club, and the public APIs /api/auth/frameio, /api/auth/frameio/callback, /api/auth/signup, /api/surf, /api/stripe/webhook, /api/cron, /api/club/enroll.
Billing-bypass prefixes
Even when a subscription is inactive, coach paths needed to fix billing or get help stay reachable -- e.g. /pricing, /billing, /settings, /org/settings, auth pages, invite acceptance, and /docs. Everything else requires an active org subscription.
Supabase clients
| File | Usage |
|---|---|
src/lib/supabase-browser.js | Client-side, anon key, RLS-scoped |
src/lib/supabase-server.js | Server-side, cookie-bound SSR client |
src/lib/supabase-admin.js | Service-role client (server only) |
Stream URL signing
Video access is gated by HMAC-signed, time-limited tokens. Clients call GET /api/video/sign (authenticated, and access-checked against the underlying resource) to get a signed URL, then pass it to /api/video/stream. Tokens have a 1-hour TTL.