Files
nis2-agile/public/forgot-password.html
T
DevEnv nis2-agileandClaude Opus 4.8 41476c88f2 [FIX] api.js: sessione scaduta → login invece di pagina bloccata "--"
Bug segnalato da un tester "sono tornato su nis2 e non funziona più" (utente/
aziende "--", inventario bloccato su Caricamento, niente FAB segnalazioni).
Causa: un 401 su /auth/refresh (refresh token scaduto/invalido) rientrava nel
handler di refresh (la condizione non escludeva l'endpoint auth) → ricorsione:
doRefreshToken() non ritornava mai, quindi logout()/redirect a login non
scattava e la pagina restava rotta. Riprodotto con puppeteer (access scaduto +
refresh invalido → dashboard "--" identica allo screenshot).

Fix: escludi /auth/refresh e /auth/login dall'auto-refresh; inoltre un 401 senza
refresh token disponibile fa logout() → login. Ora un utente che torna dopo la
scadenza del token viene mandato a login (ri-accede) invece di vedere "--".
Bug PRE-ESISTENTE in api.js (non introdotto dal V3), critico ora col push tester.
Cache-buster api.js ?v=20260627u su tutte le pagine.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:09:50 +02:00

126 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Password dimenticata - NIS2 Agile</title>
<!-- Bootstrap Italia v2.18.1 — self-hostato da /vendor (MAI CDN). Variante "-bi" della forgot-password.html. -->
<link rel="stylesheet" href="vendor/bootstrap-italia/dist/css/bootstrap-italia.min.css">
<style>
body { min-height: 100vh; display: flex; align-items: center; background: #f5f6f7; }
.auth-wrap { width: 100%; max-width: 460px; margin: 0 auto; padding: 24px; }
.auth-brand { text-align: center; margin-bottom: 24px; }
.auth-brand .brand-name { font-weight: 700; font-size: 1.5rem; color: #06c; }
.auth-brand .brand-name span { color: #17324d; }
.auth-brand .brand-sub { color: #5a6772; font-size: .9rem; margin-top: 4px; }
.ico { width: 1em; height: 1em; fill: currentColor; vertical-align: -.125em; }
</style>
<!-- PWA:start -->
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#0066CC">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/icons/favicon-32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="NIS2 Agile">
<meta name="application-name" content="NIS2 Agile">
<script src="/js/pwa.js?v=20260614" defer></script>
<!-- PWA:end -->
</head>
<body>
<main class="auth-wrap">
<div class="auth-brand">
<div class="brand-name">NIS2 <span>Agile</span></div>
<div class="brand-sub">Reimposta la tua password</div>
</div>
<div class="card card-bg has-bkg-grey shadow">
<div class="card-body">
<h1 class="h4 mb-3">Password dimenticata</h1>
<div class="alert alert-danger d-none" id="err" role="alert"></div>
<div class="alert alert-success d-none" id="ok" role="status"></div>
<p class="text-muted small mb-4">Inserisci l'indirizzo email associato al tuo account. Ti invieremo un link valido 30 minuti per impostare una nuova password.</p>
<form id="forgot-form" novalidate>
<div class="form-group">
<label for="email" class="active">Indirizzo Email</label>
<input type="email" id="email" name="email" class="form-control"
autocomplete="email" required aria-describedby="err">
</div>
<button type="submit" class="btn btn-primary w-100 mt-2" id="submit-btn">
Invia link
</button>
</form>
</div>
</div>
<p class="text-center mt-3 mb-0">
<a href="login.html">
<svg class="ico me-1" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-arrow-left"></use></svg>
Torna al login
</a>
</p>
</main>
<script src="vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
<script>
if (window.bootstrap && bootstrap.loadFonts) { bootstrap.loadFonts('vendor/bootstrap-italia/dist/fonts'); }
</script>
<!-- Stessa logica della forgot-password.html: ZERO modifiche backend -->
<script src="js/api.js?v=20260627u"></script>
<script>
const form = document.getElementById('forgot-form');
const err = document.getElementById('err');
const ok = document.getElementById('ok');
const btn = document.getElementById('submit-btn');
form.addEventListener('submit', async function(e) {
e.preventDefault();
err.classList.add('d-none');
ok.classList.add('d-none');
const email = document.getElementById('email').value.trim();
if (!email) {
err.textContent = 'Inserisci l\'indirizzo email.';
err.classList.remove('d-none');
return;
}
btn.disabled = true;
btn.textContent = 'Invio in corso...';
try {
const res = await fetch('/api/auth/forgot-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
});
const data = await res.json();
if (res.status === 429) {
err.textContent = data.message || 'Troppe richieste. Riprova più tardi.';
err.classList.remove('d-none');
} else if (data.success) {
ok.textContent = data.message;
ok.classList.remove('d-none');
form.style.display = 'none';
} else {
err.textContent = data.message || 'Errore. Riprova.';
err.classList.remove('d-none');
}
} catch (e) {
err.textContent = 'Errore di connessione al server.';
err.classList.remove('d-none');
} finally {
btn.disabled = false;
btn.textContent = 'Invia link';
}
});
</script>
</body>
</html>