Backend Clerk token verification and Supabase data layer, in one commit

`a7ea67d0` completes the Clerk migration on the backend side and consolidates document storage on Supabase, dropping the Cloudflare R2 requirement. It rewrites auth middleware, schema constraints, account deletion, user-lookup endpoints, and object storage configuration in 492 additions across 19 files.

infrastructureintegration

The auth change is the core of the commit. backend/src/middleware/auth.ts previously validated Supabase tokens; it now calls @clerk/backend to verify Clerk session JWTs. A new backend/src/lib/clerk.ts holds the Clerk client initialized with CLERK_SECRET_KEY. The resolved Clerk user id lands in res.locals.userId. Downstream code that does row.user_id === user.id works without changes because the frontend and backend now share the same Clerk id.

Clerk session tokens don't carry email, so email resolution became a separate step. The backend now calls the Clerk API with a short-lived cache. A lookup failure is non-fatal: it yields an empty result for shared-item queries rather than a 500. Sharing endpoints that previously hit Supabase's auth admin API to look up other users were repointed to Clerk's user APIs.

The schema change is a consequence of Clerk ids not being UUIDs. Columns that previously referenced auth.users (which enforces UUID format) were relaxed to plain text. The Supabase Auth signup trigger that auto-created user profiles was dropped; the backend's existing lazy profile creation handles first-login setup instead. Account deletion was rewritten to call Clerk to remove the user in addition to cleaning up database rows. The now-dead Supabase JWT helper was removed.

Storage moved from Cloudflare R2-specific env vars (R2_ENDPOINT_URL, R2_ACCESS_KEY_ID, etc.) to generic S3 vars (S3_ENDPOINT_URL, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET_NAME). The AWS SDK client code needed no changes - Supabase Storage's S3-compatible endpoint at https://<project-ref>.supabase.co/storage/v1/s3 works against the existing client unchanged.

zgbrenner was explicit about what validation exists: TypeScript build and existing frontend tests. The backend has no automated test runner. End-to-end verification requires a live Supabase project and a deployed backend. No multi-tenant isolation was added; Gary stays single-user with per-table user_id filtering.

So what Worth reading if you're evaluating the Clerk + Supabase combination for a legal-AI fork. The backend JWT verification pattern is clean and the S3-generic storage refactor is a low-risk improvement over hard-wired R2 config. The lack of backend tests is the gap: security-sensitive paths (token verification, account deletion) have no automated coverage, so verify the `requireAuth` middleware and account deletion code directly before shipping. Skip this if you're staying on Supabase Auth - the migration adds operational complexity that's only justified if Clerk's hosted auth UX or the Supabase-only infrastructure simplification matters to your deployment.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

1 commit from zgbrenner/gary, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
a7ea67d0 feat: verify Clerk tokens in backend, use Supabase for storage Claude 2026-05-20 ↗ GitHub
commit body
Complete the Clerk migration on the backend side and consolidate the data
layer onto Supabase so Gary can run without Cloudflare.

- Backend auth: requireAuth verifies Clerk session JWTs via @clerk/backend
  instead of Supabase tokens; Clerk user id flows to res.locals.userId
- Resolve user emails through the Clerk API (cached); the sharing/"people"
  endpoints now look users up via Clerk, not Supabase Auth
- Schema: user_profiles and user_api_keys store the Clerk user id as text
  and no longer depend on auth.users; drop the Supabase Auth signup trigger
- Account deletion removes the Clerk user plus the profile/api-key rows
- Storage: switch the S3 client config from R2-specific env vars to generic
  S3_* vars (with a configurable region) so Supabase Storage works
- Remove the dead frontend storage helper and the unused Supabase JWT helper
- Docs: add docs/SUPABASE_SETUP.md and update env examples / deployment docs

https://claude.ai/code/session_018GMAE164ehpBTxzBdsof9r

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-567.md from inside the repo you want the changes in.

⬇ Download capture-thread-567.md