Files
clienthub/.planning/ROADMAP.md
T
simone 8158038145 feat(auth): gate OTP email sul portale cliente (v2.3 Phases 23-25)
Il portale /client/<slug> era protetto dal solo token in URL: chiunque
ricevesse o intercettasse il link entrava, per sempre, senza identificarsi.
Ora l'admin registra le email autorizzate per cliente e il cliente si
identifica con un codice usa-e-getta prima di vedere qualsiasi dato.

- Resend 6.18.1 + src/lib/mailer.ts (Result tipizzato, mai catch silenzioso)
- migration 0015 (gia applicata a prod): client_emails, otp_codes,
  clients.sessions_valid_from. Additiva pura, conteggi verificati pre/post
- admin: sezione "Accessi al portale" in /admin/clients/[id] con whitelist
  e revoca sessioni in blocco
- gate: codice 6 cifre CSPRNG, hash SHA-256 (mai il codice in chiaro),
  TTL 15 min, max 5 tentativi, rate limit su entrambi gli endpoint,
  risposta identica per email in whitelist e non (no enumeration)
- sessione: cookie HMAC per-cliente, 90 giorni, httpOnly/secure/lax

Il gate sta in cima alla page, NON nel layout: nell'App Router il segmento
page viene renderizzato in parallelo al layout, quindi gattare nel layout
nascondeva la dashboard a schermo ma lasciava fasi, task e pagamenti nel
payload RSC dell'HTML (46907 byte -> 17594 dopo il fix). Verificato.

Verifica: build OK, 9/9 test E2E in locale contro il DB di produzione.

NON DEPLOYARE prima di: RESEND_API_KEY+RESEND_FROM su Coolify e whitelist
popolata per i 3 clienti reali (oggi vuota) - altrimenti il gate li chiude
fuori dal loro portale. Checklist in .planning/STATE.md.

SEND-01/02 (invio preventivo via email) rinviati a v2.4 su richiesta.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 12:10:33 +02:00

7.4 KiB
Raw Blame History

Roadmap: ClientHub

Milestones

  • v1.0 Client Portal & Offer System — Phases 16 (shipped 2026-06-10) — archive
  • v2.0 Business Operations Suite — Phases 710 (shipped 2026-06-13) — archive
  • v2.1 Offer Studio + CRM — Phases 1114 parziale (chiuso 2026-06-19, reset → v2.2)
  • v2.2 Sales Loop — Phases 1822 (shipped 2026-06-20) — archive
  • 🔨 v2.3 Email & Accesso — Phases 2325 (in corso)

Phases

v1.0 + v2.0 + v2.1 (Phases 117) — SHIPPED / CHIUSE

Vedi archivi:

  • milestones/v1.0-ROADMAP.md — Phases 16
  • milestones/v2.0-ROADMAP.md — Phases 710
  • Phases 11, 12, 14 — Offer Studio + CRM Attio (shipped in prod)
  • Phases 13, 15, 16, 17 — congelate/abbandonate/ri-scopate in v2.2
v2.2 Sales Loop (Phases 1822) — SHIPPED 2026-06-20
  • Phase 18: Cleanup & Consolidamento (3/3 plans) — completed 2026-06-19
  • Phase 19: Pipeline CRM Kanban (1/1 plan) — completed 2026-06-19
  • Phase 20: Knowledge Base Cliente (3/3 plans) — completed 2026-06-20
  • Phase 21: Agente AI — generazione preventivo (1/1 plan) — completed 2026-06-20
  • Phase 22: Pagina pubblica preventivo (1/1 plan, PUB-03 deferred) — completed 2026-06-20

Archivio completo: milestones/v2.2-ROADMAP.md

🔨 v2.3 — Email & Accesso (Phases 2325)

  • Phase 23: Resend Setup — SDK Resend + src/lib/mailer.ts + template OTP (l'invio preventivo è stato rinviato a v2.4 il 2026-07-28)
  • Phase 24: Schema + Whitelist Admin — Tabelle client_emails e otp_codes, admin UI gestione whitelist + revoca sessioni
  • Phase 25: OTP Gate + Sessione — Gate OTP completo, sessione 90gg, rate limiting, no enumeration

⚠️ Codice completo e testato, NON ancora in produzione (2026-07-28). Il deploy è bloccato da: RESEND_API_KEY assente su Coolify e whitelist vuota per 3 clienti su 4. Dettagli e checklist in STATE.md.

Phase Details

Phase 23: Resend Setup + Invio Preventivo

Goal: Admin può inviare il link /preventivo/[slug] via email con un click dall'admin UI Depends on: Nothing — primo uso di Resend, nessuna dipendenza DB Requirements: SEND-01, SEND-02 Success Criteria (what must be TRUE):

  1. Admin fa click su "Invia preventivo" nel dettaglio lead/preventivo e l'email parte senza uscire dall'app
  2. Il destinatario riceve un'email in italiano con nome cliente e link cliccabile al deck pubblico
  3. L'invio usa Resend con le variabili d'ambiente configurate su Coolify (RESEND_API_KEY, RESEND_FROM)
  4. In caso di errore Resend, l'admin vede un messaggio di errore chiaro nell'UI (non un crash silenzioso) Plans: TBD UI hint: yes

Phase 24: Schema + Whitelist Admin

Goal: Admin può gestire la whitelist email di ogni cliente, con le tabelle DB pronte per l'OTP gate Depends on: Phase 23 (Resend SDK già installato e variabili d'ambiente configurate) Requirements: OTP-01 Success Criteria (what must be TRUE):

  1. Admin può aggiungere una o più email alla whitelist di un cliente dalla pagina dettaglio cliente
  2. Admin può rimuovere un'email dalla whitelist di un cliente
  3. Le tabelle client_emails e otp_codes esistono in produzione (migration additive applicata via SSH prima del codice)
  4. La migration non tocca nessuna delle tabelle protette (clients, projects, payments, phases) Plans: TBD UI hint: yes

Phase 25: OTP Gate + Sessione

Goal: Il portale /client/[token]/* richiede verifica OTP email prima di mostrare la dashboard Depends on: Phase 24 (tabelle client_emails e otp_codes in prod) Requirements: OTP-02, OTP-03, OTP-04, OTP-05, OTP-06, OTP-07 Success Criteria (what must be TRUE):

  1. Cliente senza sessione OTP valida vede una schermata "inserisci la tua email" al posto della dashboard
  2. Inserita un'email in whitelist, il cliente riceve il codice OTP via Resend; inserendo il codice corretto ottiene accesso con cookie valido 30 giorni
  3. Un'email non in whitelist non riceve OTP — il messaggio d'errore mostrato è identico a quello per email valide (no enumeration)
  4. Un codice OTP non utilizzato entro 15 minuti viene rifiutato; il cliente deve richiederne uno nuovo
  5. Tentativi ripetuti sugli endpoint OTP vengono bloccati dal rate limiter (no brute force) Plans: TBD

Progress

Phase Milestone Plans Status Completed
16. Foundation → UX Overhaul v1.0 24/24 Done 2026-06-10
710. Unified Catalog → CRM Pipeline v2.0 12/12 Done 2026-06-13
11. Catalog Database-View UX v2.1 4/4 Done 2026-06-13
12. Offer Editor Tier A/B/C v2.1 5/5 Done 2026-06-18
13. Workspace Servizi Attivi v2.1 Congelata
14. CRM Attio-style & Fix v2.1 3/3 Done 2026-06-14
15. Dashboard Revenue Stats v2.1 Abbandonata
1617. Proposal AI originale v2.1 Ri-scopata in v2.2
18. Cleanup & Consolidamento v2.2 3/3 Done 2026-06-19
19. Pipeline CRM Kanban v2.2 1/1 Done 2026-06-19
20. Knowledge Base Cliente v2.2 3/3 Done 2026-06-20
21. Agente AI Preventivo v2.2 1/1 Done 2026-06-20
22. Pagina Pubblica + Deck v2.2 1/1 Done 2026-06-20
23. Resend Setup v2.3 1/1 Done 2026-07-28
24. Schema + Whitelist Admin v2.3 1/1 Done 2026-07-28
25. OTP Gate + Sessione v2.3 1/1 Done (non deployato) 2026-07-28

Requirement Coverage (v2.3)

Requirement Phase
SEND-01 Phase 23
SEND-02 Phase 23
OTP-01 Phase 24
OTP-02 Phase 25
OTP-03 Phase 25
OTP-04 Phase 25
OTP-05 Phase 25
OTP-06 Phase 25
OTP-07 Phase 25

Mapped: 9/9. No orphans.


Dependency Chain (v2.3)

Phase 23 (Resend config + SDK)
    └── Phase 24 (schema DB additive: client_emails + otp_codes)
            └── Phase 25 (OTP gate + sessione cookie 30gg)

Phase 23 first: Resend SDK e variabili d'ambiente sono infrastruttura condivisa con Phase 25 (email OTP). Phase 24 before Phase 25: le tabelle client_emails e otp_codes devono essere in prod (via SSH migration) prima del gate.


Implementation Notes (v2.3)

Migration constraint: client_emails e otp_codes sono nuove tabelle — migration additiva pura. SQL a mano (drizzle-kit generate rotto da Phase 8). Applicare via SSH tunnel PRIMA di pushare il codice dipendente.

OTP middleware layer: Il token middleware esistente (proxy.ts → /api/internal/validate-token) rimane invariato. Il gate OTP è uno strato aggiuntivo dopo la validazione del token, non un suo rimpiazzo.

Resend shared infra: La stessa istanza Resend client e le stesse variabili d'ambiente (RESEND_API_KEY, RESEND_FROM) servono sia Phase 23 (email preventivo) sia Phase 25 (email OTP). Configurare una volta in Phase 23, riusare in Phase 25.

Security invariants (Phase 25): Rate limiting su entrambi gli endpoint OTP; risposta identica per email in whitelist e non; OTP 6 cifre, scade 15 minuti, monouso (consumed_at impostato al primo uso); cookie HttpOnly, SameSite=Lax, MaxAge 30 giorni.


Roadmap created: 2026-06-21 — v2.3 Email & Accesso