# 11 — API: autenticazione JWT Leggi `CLAUDE.md`. ## Obiettivo Login unico applicativo: registrazione, login, refresh, logout, reset password, verifica email. ## Cosa fare - Hash password con argon2id. - Access token JWT breve (15 min) restituito nel body; il frontend lo tiene in memoria. - Refresh token opaco (random 256 bit) in cookie `httpOnly`, `Secure`, `SameSite=Lax`, `Domain` = dominio padre (per condividerlo tra sottodomini), path `/auth`. Rotazione a ogni refresh, con rilevamento riuso (se un token revocato viene riusato → revoca l'intera family). - Route `public/auth`: `POST /register`, `POST /login`, `POST /refresh`, `POST /logout`, `POST /forgot-password`, `POST /reset-password`, `POST /verify-email`, `GET /me`. - Registrazione: ruolo di default `customer`; `company` se viene passata ragione sociale + P.IVA; `installer` crea anche un `installer_profiles` in stato `pending`. Mai assegnabile `admin` da questa route. - Decorator `authenticate` e `requireRole(...roles)` riutilizzabili. - Rate limit stretto su login/forgot-password. Messaggi di errore che non rivelano se un'email esiste. - Invio email tramite interfaccia `EmailService` (per ora può essere un adapter che logga a console: l'implementazione arriva nel prompt 15). ## Criteri di accettazione - Test automatici (vitest + `fastify.inject`) per: registrazione, login ok/ko, refresh con rotazione, riuso di refresh token revocato → family revocata, logout, `requireRole` che blocca ruoli errati. - Nessun token o password in chiaro nel DB o nei log. - `CLAUDE.md` aggiornato con il flusso auth.