Migration Recap · Local · Apr 2026

WorkOSBetter-Auth

What just changed in sitzio.de: a new local Hono auth backend, ten file edits in the SPA, zero remaining WorkOS imports. Test bed running on two ports.

§ 01

Architecture, side by side

Before WorkOS
flowchart TB
  classDef user fill:#1c2128,stroke:#58a6ff,stroke-width:2px,color:#e6edf3,font-weight:600
  classDef spa fill:#1c2128,stroke:#d2a8ff,stroke-width:2px,color:#e6edf3,font-weight:600
  classDef cloud fill:#1c2128,stroke:#f85149,stroke-width:2px,color:#e6edf3,font-weight:600

  U["Browser"]:::user
  S["sitzio.de SPA
Vite + React"]:::spa W["WorkOS Cloud
AuthKit hosted login

their UI
their database
their brand"]:::cloud U --> S S -->|"redirect to login"| W W -->|"callback"| S
After Better-Auth
flowchart TB
  classDef user fill:#1c2128,stroke:#58a6ff,stroke-width:2px,color:#e6edf3,font-weight:600
  classDef spa fill:#1c2128,stroke:#d2a8ff,stroke-width:2px,color:#e6edf3,font-weight:600
  classDef api fill:#1c2128,stroke:#56d364,stroke-width:2px,color:#e6edf3,font-weight:600
  classDef db fill:#1c2128,stroke:#e3b341,stroke-width:2px,color:#e6edf3,font-weight:600

  U["Browser"]:::user
  S["sitzio.de SPA
Vite + React
localhost:5173"]:::spa A["auth-server
Hono + Better-Auth
localhost:3001"]:::api D[("SQLite
data/auth.db")]:::db U --> S S <-->|"fetch /api/auth/*"| A A <--> D
§ 02

Every file that changed

git status14
A
auth-server/package.json
backend deps · Hono + Better-Auth + sqlite
A
auth-server/src/auth.ts
Better-Auth config · sqlite · 7-day sessions
A
auth-server/src/index.ts
Hono entrypoint · CORS for :5173 · /api/auth/*
A
auth-server/tsconfig.json
ESM · ES2022 · strict
A
auth-server/README.md
how to run + what's intentionally not done
A
auth-server/.gitignore
node_modules · *.db · data/
A
src/lib/auth-client.ts
Better-Auth React client · talks to :3001
A
src/pages/SignInPage.tsx
German email + password form
A
src/pages/SignUpPage.tsx
German name + email + password form
M
src/App.tsx
dropped AuthKitProvider · added /sign-in + /sign-up
M
src/components/layout/Header.tsx
useAuth() → useSession() · sign-in is now a link
M
src/pages/CallbackPage.tsx
stripped to redirect-home stub
M
src/pages/DatenschutzPage.tsx
privacy section rewritten (DSGVO)
M
src/env.d.ts
VITE_WORKOS_* → VITE_AUTH_BACKEND_URL
M
vitest.config.ts
env var renamed (tests still skip auth)
M
package.json
@workos-inc/authkit-react → better-auth
M
.env.local
VITE_AUTH_BACKEND_URL=http://localhost:3001
§ 03

A real signup, step by step

localhost:5173 → localhost:3001 → data/auth.db Live flow
sequenceDiagram
  autonumber
  actor User
  participant SPA as SPA :5173
  participant API as auth-server :3001
  participant DB as SQLite

  User->>SPA: Click "Anmelden"
  SPA-->>User: /sign-up form
  User->>SPA: name + email + password
  SPA->>API: POST /api/auth/sign-up/email

  Note over API: Hash password (Argon2id)
  API->>DB: INSERT user
  API->>DB: INSERT account (with hash)
  API->>DB: INSERT session

  API-->>SPA: Set-Cookie session_token
(httpOnly, SameSite=Lax) SPA-->>User: redirect to / Note over SPA: useSession() returns user SPA-->>User: Header shows "Hi, name"
§ 04

Two terminals. That's the whole dev loop.

terminal 1 — auth backend
$ cd auth-server $ npm install added 87 packages in 4s $ npm run dev > tsx watch src/index.ts ✓ sitzio auth server listening on http://localhost:3001 CORS allow-origin: localhost:5173 DB path: ./data/auth.db
Hono + Better-Auth + SQLite
terminal 2 — Vite SPA
$ npm install removed @workos-inc/authkit-react added better-auth $ npm run dev > vite VITE v8.0.1 ready in 312 ms ➜ Local: http://localhost:5173 ➜ Network: use --host to expose press h + enter to show help
React 19 SPA
SPA fetches → backend via /api/auth/* with credentials: include
§ 05

What lives in data/auth.db

user
  • idtext · pk
  • emailtext · uniq
  • nametext
  • emailVerifiedboolean
  • createdAttimestamp
  • updatedAttimestamp
session
  • idtext · pk
  • userIdfk → user
  • tokentext
  • expiresAttimestamp
  • ipAddresstext
  • userAgenttext
account
  • idtext · pk
  • userIdfk → user
  • providerIdtext
  • accountIdtext
  • passwordargon2id hash
§ 06

Migration at a glance

14
Files touched
(9 new + 8 modified)
0
WorkOS imports
remaining
3
SQLite tables
auto-created
2
Terminals
to spin it up
§ 07

Deliberately not done yet

Email verification
Test bed only. Disabled in auth.ts until Resend is wired up.
Password reset UI
Backend supports it. SPA needs a /reset-password page.
Production deployment
Local SQLite only. Move to Coolify Node service or Hono on Cloudflare Workers + D1 for prod.
Per-site sender domain
Same as email verification — needs Resend API key + DNS via DomainOps.
Two installs. Two dev commands.
terminal 1 — backend
$ cd /root/companies/sitzio.de/auth-server $ npm install $ npm run dev
terminal 2 — SPA
$ cd /root/companies/sitzio.de $ npm install $ npm run dev