Skip to content

epic: Migrate auth from Stack Auth to Better Auth + Cloudflare deployment #15

@paccloud

Description

@paccloud

Overview

Migrate Fish Cost Calculator from Vercel + Stack Auth to Cloudflare Pages/Workers + Better Auth, keeping Neon PostgreSQL as the database. This consolidates the current dual auth system (custom JWT + Stack Auth OAuth) into a single Better Auth implementation that runs natively in Cloudflare Workers.

Why

  • Stack Auth is tied to the Vercel/Neon integration panel — not portable
  • Neon Auth has moved to Better Auth under the hood — natural migration path
  • Better Auth runs natively in CF Workers (no external API calls for auth verification)
  • Consolidates dual auth (JWT + OAuth) into one system
  • $0/month on Neon free tier (60K MAU) + Cloudflare free tier (100K req/day)

Current State

  • Auth: Dual system — custom JWT (password) + Stack Auth (Google/GitHub OAuth)
  • Database: Neon PostgreSQL via @neondatabase/serverless
  • Hosting: Vercel (serverless functions + static frontend)
  • Integrations: 2x Neon + 1x Clerk (unused) on Vercel

Current Auth Files

File Role
api/_lib/auth.js requireAuth() middleware, JWT verify, Stack Auth fallback
api/_lib/neon-auth.js Stack Auth session verification (calls api.stack-auth.com)
api/login.js Password login → JWT token
api/register.js Password registration → bcrypt hash
app/src/context/AuthContext.jsx Frontend auth state, JWT + Stack Auth session checks
app/src/config/neonAuth.js StackClientApp configuration
app/src/App.jsx StackProvider wrapper, /handler/* OAuth callback route

Protected Endpoints (8)

save-calc, saved-calcs, user-data/index, user-data/[id], contributor, export, upload-data

Database Tables

  • users — id, username, password (bcrypt), email, neon_auth_id, auth_provider, avatar_url, role
  • calculations — user_id FK
  • user_data — user_id FK
  • contributors — user_id FK

Target State

  • Auth: Better Auth (self-hosted in CF Worker) — email/password + Google/GitHub OAuth
  • Database: Neon PostgreSQL via Cloudflare Hyperdrive
  • Hosting: Cloudflare Pages (frontend) + Workers (API)
  • Auth data: Stored in Neon (neon_auth schema or custom tables)

Phases

Phase 0: Cleanup (pre-migration)

  • Remove unused Clerk integration from Vercel
  • Remove stale Neon integration (12/19/24) from Vercel
  • Fix Stack Auth trusted domains (immediate fix for current redirect error)
  • Document current env vars and their sources

Phase 1: Better Auth Setup

  • Install better-auth package
  • Configure Better Auth with Neon PostgreSQL connection
  • Set up auth schema (migrations or auto-create)
  • Configure providers: email/password, Google OAuth, GitHub OAuth
  • Test auth flows locally

Phase 2: Backend Migration

  • Replace api/_lib/neon-auth.js with Better Auth session verification
  • Replace api/_lib/auth.js requireAuth() to use Better Auth
  • Replace api/login.js with Better Auth sign-in
  • Replace api/register.js with Better Auth sign-up
  • Migrate existing users table → Better Auth user schema
  • Preserve user_id foreign keys (calculations, user_data, contributors)
  • Update all 8 protected endpoints

Phase 3: Frontend Migration

  • Replace StackProvider with Better Auth React provider in App.jsx
  • Replace app/src/config/neonAuth.js with Better Auth client config
  • Rewrite AuthContext.jsx to use Better Auth sessions
  • Update Login component (OAuth buttons + password form)
  • Remove /handler/* Stack Auth callback route
  • Add Better Auth callback route
  • Test cross-device login persistence

Phase 4: Cloudflare Deployment

  • Set up Cloudflare Pages project (frontend)
  • Set up Cloudflare Workers for API endpoints
  • Configure Hyperdrive connection to Neon PostgreSQL
  • Set up environment variables / secrets in CF dashboard
  • Configure wrangler.toml with nodejs_compat flag
  • Set up custom domain / DNS
  • Test full auth flow on CF

Phase 5: Cutover & Cleanup

  • Verify all auth flows work on Cloudflare
  • Migrate DNS to Cloudflare (if using custom domain)
  • Remove Vercel deployment
  • Remove Stack Auth env vars
  • Remove @stackframe/react dependency
  • Delete old auth files (neon-auth.js, neonAuth.js)
  • Update CLAUDE.md and documentation
  • Update env examples

User Data Migration

Existing users need to maintain access:

  • Password users: Bcrypt hashes may need re-import into Better Auth's user table (or keep custom table with adapter)
  • OAuth users: neon_auth_id maps to Stack Auth IDs — Better Auth will assign new IDs on first OAuth login. Email-based linking preserves account continuity.
  • Saved data: calculations, user_data, contributors tables use user_id FK — must map old IDs to new Better Auth user IDs

Risk Mitigation

  • Better Auth supports custom database adapters — can point at existing users table structure
  • Email-based identity linking means OAuth users reconnect automatically on first login
  • Can run both auth systems in parallel during transition (current requireAuth() already supports fallback pattern)
  • Neon database is platform-agnostic — no data migration needed

References

Labels

epic, auth, cloudflare, migration

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions