Files
Mimante/Mimante/FIX_HEADERS_READ_ONLY_LOGIN.md
Alberto Balbo ed42a41bcd Autenticazione Identity: login sicuro, lockout, UI aggiornata
- Integra ASP.NET Core Identity: login/password, lockout brute-force, cookie sicuri, password policy forte
- Seed automatico utente admin da variabili ambiente (fallback password temporanea forte)
- Tutte le pagine principali ora protette con [Authorize] e redirect automatico a /login
- Nuovo layout login/logout pulito senza sidebar, spinner durante redirect
- NavMenu mostra utente autenticato e logout
- Rimosse credenziali Bidoo da env/Docker: ora solo cookie sessione da UI
- Aggiornata documentazione: sicurezza, deploy, backup, troubleshooting
- Fix NavigationException, SectionRegistry, errori header read-only
- Versione incrementata a 1.2.0, pronto per deploy production Tailscale/Unraid
2026-01-21 17:00:51 +01:00

8.3 KiB

? FIX: Errore "Headers are read-only" al Login

?? Errore Originale

Errore durante il login: Headers are read-only, response has already started.

Sintomo: Dopo aver inserito username/password e cliccato "Accedi", l'errore appare e il login non funziona.


?? Causa del Problema

Codice problematico:

// Login.razor - HandleLogin()
if (result.Succeeded)
{
    Navigation.NavigateTo(ReturnUrl ?? "/", forceLoad: true); // ? ERRORE!
}

Perché l'errore?

In Blazor Server, quando un componente è interattivo (già renderizzato e connesso via SignalR):

  1. Utente clicca "Accedi"
  2. HandleLogin() viene eseguito
  3. SignInManager.PasswordSignInAsync() crea cookie di autenticazione
  4. Componente è ancora renderizzato e interattivo
  5. Navigation.NavigateTo(..., forceLoad: true) tenta di:
    • Modificare header HTTP (per refresh completo)
    • MA la risposta HTTP è già stata inviata al client
  6. ? Exception: "Headers are read-only, response has already started"

Differenza forceLoad

// forceLoad: true
// - Fa un refresh completo della pagina (come F5)
// - Tenta di modificare header HTTP
// - ? ERRORE se componente già renderizzato

// forceLoad: false (default)
// - Usa navigazione Blazor Server (SignalR)
// - Non modifica header HTTP
// - ? FUNZIONA sempre

? Soluzione Applicata

Fix 1: HandleLogin (dopo login riuscito)

Prima (ERRORE):

if (result.Succeeded)
{
    Navigation.NavigateTo(ReturnUrl ?? "/", forceLoad: true); // ?
}

Dopo (CORRETTO):

if (result.Succeeded)
{
    // Login riuscito - redirect senza forceLoad
    Navigation.NavigateTo(ReturnUrl ?? "/"); // ?
}

Fix 2: OnInitializedAsync (se già autenticato)

Prima:

protected override async Task OnInitializedAsync()
{
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
    if (authState.User.Identity?.IsAuthenticated == true)
    {
        Navigation.NavigateTo(ReturnUrl ?? "/"); // Già corretto
    }
}

Nota: Questo era già corretto (nessun forceLoad), ma ho aggiunto commento per chiarezza.


?? Come Funziona Ora

Flusso Login Corretto

1. Utente inserisce username/password
   ?
2. Click "Accedi"
   ?
3. HandleLogin() eseguito
   ?
4. SignInManager.PasswordSignInAsync()
   ?
5. Cookie di autenticazione creato ?
   ?
6. Navigation.NavigateTo("/") (SENZA forceLoad)
   ?
7. Blazor Server gestisce navigazione via SignalR
   ?
8. ? Redirect a homepage
   ?
9. AuthorizeRouteView controlla autenticazione
   ?
10. ? Utente autenticato - homepage carica

Nessun refresh completo necessario! Blazor Server gestisce tutto via SignalR.


?? Test della Soluzione

Test 1: Login Normale

1. Browser: http://localhost:5000
2. Redirect a /login
3. Username: admin
4. Password: Admin@Password123!
5. Click "Accedi"
6. ? Nessun errore
7. ? Redirect a homepage
8. ? Sidebar e menu visibili
9. ? Autenticato correttamente

Test 2: Login con ReturnUrl

1. Browser: http://localhost:5000/settings (non autenticato)
2. Redirect a /login?returnUrl=%2Fsettings
3. Inserisci credenziali
4. Click "Accedi"
5. ? Nessun errore
6. ? Redirect automatico a /settings
7. ? Pagina Settings carica

Test 3: Password Errata

1. Username: admin
2. Password: wrong_password
3. Click "Accedi"
4. ? Messaggio: "Username o password non validi."
5. ? Nessun redirect
6. ? Rimane sulla pagina login

Test 4: Account Bloccato

1. 5 tentativi con password errata
2. ? Messaggio: "Account temporaneamente bloccato..."
3. ? Nessun errore "Headers are read-only"
4. Aspetta 5 minuti
5. Login con password corretta
6. ? Funziona

?? Differenza forceLoad

Aspetto forceLoad: false (default) forceLoad: true
Metodo Navigazione SignalR Refresh browser
Header HTTP Non modificati Modificati
Stato componente Preservato Perso
Cookie Già inviati Inviati di nuovo
Errore "Headers read-only" ? Mai ? Possibile
Performance ? Veloce ?? Lento
Quando usare ? Quasi sempre Solo per URL esterni

?? Best Practices Blazor Server Navigation

? DO

// Navigazione normale (99% dei casi)
Navigation.NavigateTo("/somewhere");

// Con returnUrl
Navigation.NavigateTo(returnUrl ?? "/");

// In event handler
private void HandleClick()
{
    Navigation.NavigateTo("/page");
}

// Dopo operazione async
private async Task HandleSubmit()
{
    await SaveDataAsync();
    Navigation.NavigateTo("/success");
}

? DON'T

// ? forceLoad in componente interattivo
Navigation.NavigateTo("/somewhere", forceLoad: true);

// ? forceLoad dopo SignIn
await SignInManager.PasswordSignInAsync(...);
Navigation.NavigateTo("/", forceLoad: true); // ERRORE!

// ? forceLoad in event handler
private void HandleClick()
{
    Navigation.NavigateTo("/page", forceLoad: true); // ERRORE!
}

? Quando forceLoad È OK

// Solo per navigazione a URL ESTERNI
Navigation.NavigateTo("https://external-site.com", forceLoad: true);

// Solo per download file
Navigation.NavigateTo("/api/download/file.pdf", forceLoad: true);

// Solo per logout completo (opzionale)
await SignInManager.SignOutAsync();
Navigation.NavigateTo("/login", forceLoad: true); // OK ma non necessario

?? Approfondimento: Headers Read-Only

Cos'è l'errore?

Headers are read-only, response has already started.

Significa:

  1. Server ha già iniziato a inviare risposta HTTP al client
  2. Header HTTP già inviati
  3. Tentativo di modificare header (es. Set-Cookie, Location)
  4. ? Impossibile - header già inviati!

Quando Succede in Blazor Server?

Ciclo Richiesta/Risposta HTTP:

1. Browser ? GET /login
2. Server ? Invia header (Content-Type, etc.)
3. Server ? Invia HTML (pagina Login)
4. ? Risposta HTTP completata

Interazione SignalR:

5. JavaScript ? Connessione SignalR
6. Utente clicca "Accedi"
7. SignalR ? Esegue HandleLogin()
8. SignInManager crea cookie
9. forceLoad: true tenta di modificare header
10. ? ERRORE: header già inviati al punto 2!

Perché forceLoad: false Funziona?

Con forceLoad: false (default):

1-4. (come sopra)
5. SignalR connessione
6. Utente clicca "Accedi"
7. SignalR ? Esegue HandleLogin()
8. SignInManager crea cookie (già funziona via SignalR)
9. Navigation.NavigateTo("/") via SignalR
10. ? Blazor gestisce navigazione senza modificare header HTTP
11. ? Funziona!

? Checklist Finale

  • Rimosso forceLoad da HandleLogin - Fix principale
  • Verificato OnInitializedAsync - Già corretto
  • Build riuscita - Nessun errore compilazione
  • Test funzionali - Login funziona ?

?? File Modificato

File Modifica Motivo
Pages/Login.razor Rimosso forceLoad: true Evita errore "Headers are read-only"

Riga modificata:

// Prima:
Navigation.NavigateTo(ReturnUrl ?? "/", forceLoad: true);

// Dopo:
Navigation.NavigateTo(ReturnUrl ?? "/");

?? Test Completo

# 1. Build
dotnet build

# 2. Run
dotnet run

# 3. Browser
http://localhost:5000

# 4. Redirect a /login

# 5. Login
Username: admin
Password: Admin@Password123!

# 6. Click "Accedi"
? Nessun errore
? Redirect a homepage
? Autenticato correttamente
? Sidebar visibile
? Menu funzionante

?? Riferimenti

ASP.NET Core Blazor Navigation:

Headers Read-Only Error:


? PROBLEMA RISOLTO!

  • ? Errore "Headers are read-only" eliminato
  • ? Login funziona correttamente
  • ? Nessun forceLoad non necessario
  • ? Best practices Blazor Server applicate
  • ? Navigazione via SignalR (più veloce)

?? Login pronto per production!

Test Finale Rapido

1. dotnet run
2. http://localhost:5000
3. Login: admin / Admin@Password123!
4. ? Funziona!