docs(adr): adopt Better Auth, replacing Clerk

Reasons (full ADR in docs/decisions/0001-auth-provider.md):
- KetoPath targets EU users with Art. 9 GDPR data; Clerk on US infra
  raises Schrems II concerns
- Free, self-hostable, stays in our Postgres
- TypeScript-first, fits Next.js + Fastify + Prisma stack

CLAUDE.md updated:
- Tech stack: Better Auth on EU Postgres
- "Cosa NON fare mai": no manual password hashing
- References: Better Auth docs link

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucianoandClaude Opus 4.7 committed 2026-04-29 12:44:21 +02:00
1 parent 72d47da453
commit e9f1a55a5b
2 files changed
+78 -3

No files matched your search

+3 -3
View File
@@ -26,7 +26,7 @@ Il documento di prodotto completo è in `docs/PRD_KetoPath.docx`. Quando devi pr
**Backend** **Backend**
- Node.js + Fastify + TypeScript - Node.js + Fastify + TypeScript
- Prisma come ORM - Prisma come ORM
- Auth gestita via Clerk (no roll-your-own) - Auth gestita via Better Auth, self-hostata su Postgres EU (vedi `docs/decisions/0001-auth-provider.md`)
**Database & infrastruttura** **Database & infrastruttura**
- PostgreSQL 15+ come DB principale - PostgreSQL 15+ come DB principale
@@ -156,7 +156,7 @@ pnpm test:e2e # Playwright
## Cosa NON fare mai ## Cosa NON fare mai
- Salvare password in chiaro (Clerk gestisce auth, non implementarla a mano). - Salvare password in chiaro o reimplementare l'hashing manualmente (Better Auth gestisce hashing scrypt, sessioni e flussi OAuth — non riscriverli).
- Loggare dati di salute fuori dal DB cifrato. - Loggare dati di salute fuori dal DB cifrato.
- Usare `eval()` o `new Function()` con input utente. - Usare `eval()` o `new Function()` con input utente.
- Disabilitare feature di sicurezza (CSP, CORS, rate limiting) per "comodità di sviluppo". - Disabilitare feature di sicurezza (CSP, CORS, rate limiting) per "comodità di sviluppo".
@@ -199,7 +199,7 @@ Queste decisioni non sono ancora finalizzate. Se le tocchi, aprire un ADR in `do
- PRD completo: `docs/PRD_KetoPath.docx` - PRD completo: `docs/PRD_KetoPath.docx`
- Documentazione Next.js: https://nextjs.org/docs - Documentazione Next.js: https://nextjs.org/docs
- Documentazione Prisma: https://www.prisma.io/docs - Documentazione Prisma: https://www.prisma.io/docs
- Documentazione Clerk: https://clerk.com/docs - Documentazione Better Auth: https://better-auth.com/docs
- shadcn/ui: https://ui.shadcn.com - shadcn/ui: https://ui.shadcn.com
- GDPR e dati sanitari: https://www.garanteprivacy.it/ - GDPR e dati sanitari: https://www.garanteprivacy.it/
+75
View File
@@ -0,0 +1,75 @@
# ADR 0001 — Auth provider
- **Status**: accepted
- **Date**: 2026-04-29
- **Decision makers**: Luciano (PO), Claude
- **Supersedes**: scelta iniziale Clerk in `CLAUDE.md`
## Contesto
KetoPath è una web app che tratta dati di salute (peso, misure, foto progress, sintomi) — categoria particolare di dati personali ex art. 9 GDPR. Inoltre l'app, per posizionamento commerciale, è destinata **esclusivamente al mercato italiano/europeo**.
La scelta iniziale in `CLAUDE.md` era Clerk, ma:
1. **Schrems II**: Clerk è un SaaS USA. Anche con DPA + SCC, il transfer di dati personali (soprattutto sanitari) verso gli USA è una zona grigia per il Garante italiano. Un eventuale incidente o un'ispezione non avrebbe una difesa pulita.
2. **Costo**: oltre 10k MAU il prezzo scala in modo non banale per un MVP a budget contenuto (PRD §16.3).
3. **Vendor lock-in**: spostarsi da Clerk dopo l'integrazione è oneroso (sessions, password reset, OAuth tutto sul loro storage).
## Decisione
Adottare **[Better Auth](https://better-auth.com)** come libreria di autenticazione self-hostata, con i seguenti vincoli e configurazione:
- **Storage**: tabelle `User`, `Session`, `Account`, `Verification` nello stesso Postgres del resto dell'app, gestito da `@ketopath/db` (Prisma).
- **Residenza dati**: ovunque ospiteremo Postgres, deve essere in regione **EU**. Per l'MVP: locale (Mac); per la produzione (Render/Fly.io): regione EU obbligatoria.
- **Metodi di autenticazione**:
- Email + password (hashing scrypt gestito da Better Auth)
- Google OAuth (Schrems II non rilevante perché l'utente sceglie attivamente di delegare a Google)
- **Email verification**: disattivata per MVP per non bloccare onboarding/test; verrà attivata prima del lancio pubblico (V1) insieme a SendGrid.
- **MFA**: posticipata a V1.
## Conseguenze
### Positive
- Tutti i dati personali (incluse credenziali in formato hash) restano nel database controllato da noi. Difendibile in audit GDPR.
- Zero costi ricorrenti su un servizio esterno per auth.
- Nessun vendor lock-in: se Better Auth diventa irraggiungibile, possiamo sostituirla con altra libreria mantenendo lo schema.
- Stack 100% TypeScript, integrazione naturale con Next.js (App Router) e Fastify.
### Negative
- **Responsabilità di sicurezza più alta**: dobbiamo aggiornare la libreria con regolarità, monitorare le advisories, gestire il `BETTER_AUTH_SECRET` come segreto critico.
- **Funzionalità da costruire**: alcune feature che Clerk dava out-of-the-box (UI sign-in pre-fatto, gestione MFA hardware, dashboard utenti) richiedono codice nostro.
- **Maturità libreria**: Better Auth è progetto giovane (2024+), meno battle-tested rispetto a Auth.js o Clerk. Mitigazione: review delle release, smoke test E2E sul flusso auth, fallback a Auth.js se la libreria viene abbandonata.
## Alternative considerate
| Opzione | Motivo del NO |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Clerk | Transfer USA — Schrems II problematico per dati sanitari italiani |
| Auth0 | Costoso oltre 7k MAU; stesso problema USA di Clerk |
| Supabase Auth (cloud) | EU regions ok ma vendor SaaS aggiuntivo che non serve |
| Supabase Auth (self-hosted, GoTrue) | Overhead operativo (Docker, configurazione separata) non giustificato per MVP |
| Auth.js (NextAuth.js) | Tecnicamente valido, più maturo, ma API meno pulita e integrazione Fastify-side scomoda (è Next.js-first); preferito Better Auth per coerenza tra web e api |
| Lucia | Deprecata a marzo 2025 |
| Keycloak | Overkill — JVM, ops dedicato, target enterprise |
| Ory Kratos | Buona alternativa EU-native (Germania), ma overhead operativo simile a Supabase self-hosted; rivalutabile se Better Auth diventasse insostenibile |
## Implementazione
Tracciata in 6 fasi:
- **Fase 0** (questo ADR + aggiornamento `CLAUDE.md`)
- **Fase 1**: schema Prisma + migrazione `add_auth_tables`
- **Fase 2**: pacchetto `@ketopath/auth` con istanza Better Auth condivisa
- **Fase 3**: integrazione Next.js (handler, client, pagine sign-in/sign-up)
- **Fase 4**: integrazione Fastify (plugin di sessione, endpoint `GET /me`)
- **Fase 5**: test unit + E2E sign-up/sign-in
- **Configurazione OAuth Google**: a fine progetto, quando il PO fornirà le credenziali
## Vincoli operativi da rispettare
- `BETTER_AUTH_SECRET` deve essere identico tra `apps/web` e `apps/api` (è la chiave di firma della session).
- `BETTER_AUTH_SECRET` non va mai committato; va caricato via env in produzione (Vercel + Render secrets).
- Lo schema delle tabelle auth segue la convenzione Better Auth — non rinominare colonne arbitrariamente per non rompere la libreria al prossimo upgrade.
- Tutti i test E2E che toccano l'auth devono usare account creati nel test stesso (no fixture utenti reali).