Phase 2: Auth.js v5 cutover, replace Supabase Auth

↗ view on GitHub · Claude · 2026-05-15 · b4f7697f

Frontend authentication switches from Supabase Auth to Auth.js v5
with a Credentials provider. The Supabase Auth code paths are fully
removed. The Supabase JS client is no longer present in the frontend
bundle. Microsoft Entra OIDC will be added as a second provider in
the next sub-phase.

Session model:
- Session JWT is HS256-signed with AUTH_SECRET, stored in an httpOnly
  cookie set by Auth.js. The browser cannot read it.
- For backend API calls the browser obtains a short-lived (5 minute)
  HS256 JWT from GET /api/session-token. The Next.js server mints it
  from the verified Auth.js session, signed with the same AUTH_SECRET.
- The backend requireAuth middleware verifies any bearer HS256 JWT
  with the shared AUTH_SECRET. No Supabase Auth lookup is performed.

Backend changes:
- New routes/auth.ts: POST /register, POST /verify-credentials, GET /me.
- New lib/users.ts: centralised user creation, sign-in verification,
  email-domain allowlist enforcement, bootstrap-admin promotion.
- New lib/passwords.ts: bcryptjs at cost 12, min length 12.
- New lib/authToken.ts: jose-based HS256 verify/sign.
- middleware/auth.ts rewritten to verify Auth.js JWT. New
  requireAdmin middleware checks res.locals.userRole.
- BOOTSTRAP_ADMIN_EMAIL env grants admin role on boot and on first
  matching registration.
- /ready endpoint pings Postgres before reporting ready.
- Sign-in responses are deliberately indistinguishable between
  "no such user" and "wrong password" to prevent user enumeration.

Frontend changes:
- New auth.ts wires NextAuth Credentials provider with custom HS256
  encode/decode.
- /api/auth/[...nextauth]/route.ts exposes Auth.js handlers.
- /api/session-token mints short-lived backend bearers.
- middleware.ts redirects unauthenticated requests to /login for any
  path outside the public allowlist.
- AuthContext.tsx is now a thin shim around useSession; the existing
  useAuth() API is preserved so 30+ call sites keep working unchanged.
- providers.tsx wraps the tree in SessionProvider.
- login and signup pages rewritten for the new flow. Signup calls
  POST /auth/register then signIn("credentials"). Password minimum
  bumped to 12.
- mikeApi.ts and seven other files migrated to a shared
  getSessionToken() helper. None of them call Supabase directly.
- Deleted dead frontend/src/lib/{auth,supabase}.ts.
- Uninstalled @supabase/supabase-js, @supabase/auth-helpers-nextjs,
  @supabase/auth-js from the frontend.

Docs:
- docs/developer/07-auth.md documents the new flow end to end with
  a sequence diagram.
- docs/security/03-encryption.md records every cryptographic primitive
  and rotation procedure.
- Instructions decisions log and CHANGELOG updated.

Note: backend routes that still use the Supabase JS client for
PostgREST data queries are unchanged in this commit. The Supabase JS
client is retained on the backend as a transitional dependency.
Migration onto plain pg is the next sub-phase.
Repository cpatpa/PIP
Author Claude <noreply@anthropic.com>
Authored
Parents 76691b24
Stats 35 files changed , +1375 , -465
Part of Phase 2 - Auth.js v5 + Entra OIDC + password reset

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-b4f7697f.md from inside the repo you want the change in.

⬇ Download capture-commit-b4f7697f.md