# Piano implementativo — Welcome Email & Onboarding Sicurezza
**Progetto:** Evoluzione Azienda — Area Admin  
**Data:** 05/05/2026  
**Scope:** Invio mail di benvenuto alla creazione account + enforcement cambio password e 2FA obbligatoria

---

## Contesto

L'infrastruttura email è già completamente operativa:
- **nodemailer** configurato in `backend/src/services/emailService.js`
- SMTP configurabile via pannello admin (settings DB) o variabili `.env`
- Pattern consolidato: `sendContactEmail`, `sendApplicationEmail`, `sendTestEmail`

Tutto il lavoro nuovo si integra in questo stack senza introdurre dipendenze aggiuntive.

---

## Obiettivi

1. Alla creazione di un nuovo account admin, inviare automaticamente una mail di benvenuto con:
   - password temporanea assegnata
   - obbligo di cambio password al primo accesso
   - obbligo di attivazione 2FA al primo accesso
2. Template email separato dal controller (file dedicato)
3. Enforcement degli obblighi direttamente nel flusso di login
4. Feedback chiaro in UI (creazione utente ≠ invio email)

---

## Step 1 — Migrazione DB

**File:** `backend/src/schema.sql` (documentazione) + `backend/src/index.js` (migrazione automatica)

Aggiungere due colonne alla tabella `users`:

```sql
ALTER TABLE users
  ADD COLUMN must_change_password  TINYINT(1) NOT NULL DEFAULT 0 AFTER totp_enabled,
  ADD COLUMN force_2fa_required    TINYINT(1) NOT NULL DEFAULT 0 AFTER must_change_password;
```

- `must_change_password = 1` → utente deve cambiare la password prima di accedere all'area admin
- `force_2fa_required = 1` → utente deve attivare la 2FA prima di accedere all'area admin
- Entrambi vengono impostati a `1` alla creazione di ogni nuovo account
- Vengono azzerati dall'utente stesso completando i rispettivi step

La migrazione viene aggiunta alla funzione `seedUsersTable()` in `index.js` con `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` (no-op se già presente).

---

## Step 2 — Template email separato

**File nuovo:** `backend/src/emails/welcomeTemplate.js`

Esporta una singola funzione pura:

```js
export function buildWelcomeEmail({ name, email, password, adminUrl }) {
  return {
    subject: `Benvenuto nell'area admin — Evoluzione Azienda`,
    html: `...` // HTML completo con branding blu #054d8a
  }
}
```

Contenuto del template:
- Header con logo/branding Evoluzione Azienda (stile coerente con le altre mail esistenti)
- Nome e email dell'account
- Password temporanea in evidenza (box colorato)
- 3 avvisi obbligatori numerati:
  1. Cambio password obbligatorio al primo accesso
  2. Attivazione 2FA obbligatoria al primo accesso
  3. Non condividere queste credenziali
- Link diretto al pannello admin (`adminUrl`)
- Footer con data, ora e firma Evoluzione Azienda S.r.l.s.

Il controller non tocca mai l'HTML — riceve solo dati strutturati, il template fa il resto.

---

## Step 3 — Funzione `sendWelcomeEmail` in emailService.js

**File:** `backend/src/services/emailService.js`

Aggiungere in fondo al file (stesso pattern delle funzioni esistenti):

```js
export async function sendWelcomeEmail({ name, email, password, adminUrl }) {
  try {
    const { subject, html } = buildWelcomeEmail({ name, email, password, adminUrl })
    const transport = await createTransporter()
    const from      = await getFromAddress('Admin')
    await transport.sendMail({ from, to: email, subject, html })
    return { success: true }
  } catch (err) {
    console.error('[EvoAz Email] sendWelcomeEmail:', err.message)
    return { success: false, error: err.message }
  }
}
```

Import aggiunto in testa: `import { buildWelcomeEmail } from '../emails/welcomeTemplate.js'`

---

## Step 4 — Modifica `createUser` in usersController.js

**File:** `backend/src/controllers/usersController.js`

Logica aggiornata:

```
1. Ricevi { email, name, password?, sendWelcome? } dal body
2. Se password non fornita → genera password temporanea robusta (12 chars, mix)
3. Hash bcrypt della password
4. INSERT con must_change_password=1, force_2fa_required=1
5. Se sendWelcome === true (o di default true):
   - Chiama sendWelcomeEmail({ name, email, password, adminUrl })
   - emailSent = result.success
6. Risposta 201:
   { message, id, emailSent, tempPasswordGenerated }
```

**Generatore password temporanea** (inline, no dipendenze):
```js
function generateTempPassword() {
  const chars = 'ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$'
  let pwd = ''
  for (let i = 0; i < 12; i++) {
    pwd += chars[Math.floor(Math.random() * chars.length)]
  }
  return pwd
}
```

> **Nota sicurezza:** la password temporanea viene inviata via email e mai loggata.  
> Dopo il cambio password, l'utente non può più recuperarla — in caso di smarrimento serve un reset admin.

---

## Step 5 — Enforcement nel login (authController.js)

**File:** `backend/src/controllers/authController.js`

### `login` (credenziali)

Dopo la verifica bcrypt, prima di emettere il token, leggere i flag:

```js
if (user.must_change_password) {
  // Token con claim speciale — accesso limitato
  const token = jwt.sign(
    { userId: user.id, type: 'onboarding', reason: 'must_change_password' },
    JWT_SECRET,
    { expiresIn: '30m' }
  )
  return res.json({ requiresOnboarding: true, reason: 'must_change_password', token })
}
```

### `verify2fa` (dopo codice TOTP)

Stessa logica dopo la verifica OTP — se l'utente ha passato il 2FA ma ha ancora `force_2fa_required = 1` (non dovrebbe mai succedere, ma per coerenza) → già risolto dal fatto che ha attivato il 2FA.

Se invece `force_2fa_required = 1` e `totp_enabled = 0`:
```js
// Dopo emissione token full → anche se arriva qui (es. 2FA appena resettato da admin)
// il flag viene controllato nel middleware
```

### `changePassword` — aggiunta logica onboarding

Se il token è di tipo `onboarding` con reason `must_change_password`:
- Consentire il cambio password (endpoint dedicato o quello esistente ampliato)
- Dopo cambio: `UPDATE users SET must_change_password = 0 WHERE id = ?`
- Restituire token full normale

### Nuovo endpoint `POST /auth/complete-onboarding-pwd`

Protetto da `onboardingMiddleware` (vedi Step 6). Riceve `{ newPassword }`, aggiorna la password e azzera il flag, restituisce token full.

---

## Step 6 — Middleware onboarding

**File:** `backend/src/middleware/auth.js`

Aggiungere `onboardingMiddleware` accanto ad `authMiddleware`:

```js
export function onboardingMiddleware(req, res, next) {
  const token = // estrai come authMiddleware
  const decoded = jwt.verify(token, JWT_SECRET)
  if (decoded.type !== 'onboarding') return res.status(401).json({ error: 'Token non valido.' })
  req.user = decoded
  next()
}
```

`authMiddleware` già blocca token di tipo `onboarding` (`type !== 'full'`), quindi non serve modifica.

---

## Step 7 — Nuova rotta onboarding

**File:** `backend/src/routes/allRoutes.js`

```js
router.post('/auth/complete-onboarding-pwd', onboardingMiddleware, completeOnboardingPassword)
```

---

## Step 8 — UI Admin (AdminView.vue)

### 8a — Form creazione utente

Aggiungere sotto al campo password:

```
[ ] Genera password temporanea automaticamente
[x] Invia email di benvenuto
```

- Se "Genera automaticamente" è spuntato → campo password diventa disabilitato e il backend genera
- Checkbox invio email: default `true`

Feedback dopo creazione (due messaggi distinti):
- ✅ "Utente creato con successo."
- ✅ "Email di benvenuto inviata a mario@esempio.it." oppure ⚠️ "Email non inviata (SMTP non configurato o errore). L'utente è stato creato — invia l'email manualmente."

### 8b — Pulsante "Reinvia email di benvenuto"

In tabella utenti, per ogni riga, aggiungere icona ✉ che chiama un nuovo endpoint:

```
POST /admin/users/:id/send-welcome
```

Utile in caso di primo invio fallito o smarrimento credenziali temporanee.

> **Attenzione:** il reinvio genera una nuova password temporanea, aggiorna l'hash in DB e rimanda l'email. Non riusa la vecchia password (non recuperabile).

### 8c — Step onboarding nel login (loginStep)

Il sistema `loginStep` in AdminView già gestisce `'credentials'` e `'2fa'`. Aggiungere:

- `'change-password'` → step che mostra form nuova password con messaggio "Il tuo account è nuovo. Devi impostare una password personale prima di continuare."
- `'setup-2fa-required'` → step che forza l'attivazione 2FA (riusa il form QR code già esistente ma senza possibilità di skip)

Flusso login completo con tutti i casi:

```
[credentials] → ok
  ├── must_change_password=1 → [change-password] → cambio → [setup-2fa-required]? → dashboard
  ├── totp_enabled=1          → [2fa] → ok → [setup-2fa-required]? → dashboard
  └── normale                 → dashboard
```

---

## Step 9 — Nuovo endpoint reinvio welcome

**File:** `backend/src/controllers/usersController.js`

```js
export async function resendWelcomeEmail(req, res) {
  const id = parseInt(req.params.id, 10)
  // Fetch user, genera nuova temp password, aggiorna hash + flags, invia email
  // Risposta: { message, emailSent }
}
```

**Route:** `POST /admin/users/:id/send-welcome` (protetta da `authMiddleware`)

---

## Riepilogo file modificati / creati

| File | Azione |
|------|--------|
| `backend/src/emails/welcomeTemplate.js` | **Nuovo** — template HTML mail di benvenuto |
| `backend/src/services/emailService.js` | **Modifica** — aggiunta `sendWelcomeEmail` |
| `backend/src/controllers/usersController.js` | **Modifica** — `createUser` con temp pwd + email; nuovo `resendWelcomeEmail` |
| `backend/src/controllers/authController.js` | **Modifica** — enforcement `must_change_password`; nuovo `completeOnboardingPassword` |
| `backend/src/middleware/auth.js` | **Modifica** — aggiunta `onboardingMiddleware` |
| `backend/src/routes/allRoutes.js` | **Modifica** — nuove route onboarding e reinvio welcome |
| `backend/src/index.js` | **Modifica** — migrazione automatica colonne `must_change_password`, `force_2fa_required` |
| `backend/src/schema.sql` | **Modifica** — documentazione colonne aggiunte |
| `frontend/src/views/AdminView.vue` | **Modifica** — form creazione utente + step onboarding login |

---

## Considerazioni sicurezza

| Aspetto | Scelta |
|---------|--------|
| Password in chiaro nell'email | Accettabile per password **temporanea** con scadenza funzionale (cambio obbligatorio al primo accesso) |
| Password loggata | ❌ Mai — non passa per console o DB in chiaro |
| Token onboarding | TTL 30 minuti — accesso limitato al solo endpoint cambio password |
| Reinvio welcome | Genera nuova password, invalida la precedente (hash aggiornato) |
| 2FA obbligatoria | Bloccante lato frontend dopo cambio password — non bypassabile |

---

## Evolutivo suggerito (post-MVP)

- **Link di attivazione con token monouso** al posto della password in chiaro (magic link con scadenza 24h)
- **Audit log** eventi sicurezza: account creato, welcome inviata, password cambiata, 2FA attivata
- **Rate limiting** su endpoint cambio password e setup 2FA (già presente su login)
- **Notifica admin** quando un utente completa l'onboarding
