React
Middleware per l’autenticazione in Next.js
Il middleware filtra le richieste, non autorizza: come leggere il cookie, verificare il token e proteggere anche le Server Actions.
In sintesi
- Il middleware filtra, la pagina autorizza: il permesso si verifica dove il dato viene letto.
- La presenza di un cookie non prova niente: la firma del token si controlla sul server.
- Ogni controllo nel middleware gira su ogni richiesta: tienilo locale e veloce.
Il middleware legge il cookie di sessione e reindirizza prima che la pagina venga renderizzata, ma non è un controllo di autorizzazione. Il permesso si verifica nella pagina o nell’azione che legge il dato.
Cosa può fare il middleware
- Leggere cookie, header e URL, e decidere se lasciar passare la richiesta.
- Riscrivere il percorso, redirigere e aggiungere header alla risposta.
- Girare prima del rendering, quindi anche sulle route che non esistono.
- Non interrogare un database con un driver Node e non conoscere i permessi aggiornati al millisecondo.
npm install jose
Un matcher, poi il redirect
// middleware.ts
import { NextResponse, type NextRequest } from "next/server";
const PUBLIC = ["/login", "/signup"];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (PUBLIC.some((path) => pathname.startsWith(path))) return NextResponse.next();
if (!request.cookies.get("session")) {
const url = request.nextUrl.clone();
url.pathname = "/login";
url.searchParams.set("next", pathname);
return NextResponse.redirect(url);
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
Il matcher è parte della protezione: senza esclusione degli asset, il middleware gira su ogni immagine e su ogni file CSS. Next.js 16 ha rinominato il file in proxy.ts: la funzione e il matcher si comportano allo stesso modo, cambia il nome.
Il cookie non è una prova
Un cookie si crea dal browser in dieci secondi. Il middleware qui controlla solo che il cookie esista, e questo va bene come prima scrematura: chi non ha il cookie non arriva alla pagina. La firma del token, invece, va verificata sul server, con una chiave che il client non possiede.
// lib/session.ts
import { jwtVerify } from "jose";
const secret = new TextEncoder().encode(process.env.SESSION_SECRET);
export async function verifySession(token?: string) {
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as { userId: string; role: string };
} catch {
return null;
}
}
Verifica dove il dato viene letto
La pagina non si fida del fatto che il middleware sia passato: ripete la verifica e filtra la query per l’utente della sessione. Se domani qualcuno cambia il matcher, il controllo resta. Questa è la differenza tra un cancello e una recinzione.
// app/dashboard/page.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { verifySession } from "@/lib/session";
export default async function DashboardPage() {
const session = await verifySession((await cookies()).get("session")?.value);
if (!session) redirect("/login");
const invoices = await db.invoice.findMany({ where: { userId: session.userId } });
return ;
}
Il costo di girare su tutto
Il middleware esegue su ogni richiesta che corrisponde al matcher, prima che la route esista. Una fetch esterna o una query al database lì dentro rallenta ogni pagina del sito, comprese quelle pubbliche. Tieni il controllo locale: un cookie, un header, un confronto di stringhe.
Un blocco di login non è un sistema di sessioni
I blocchi di login e di autenticazione danno form, stati e markup, non la gestione del token. Il record Sign In mostra la parte visibile di un flusso completo. La sessione la costruisci tu: firma, scadenza, revoca.
Errori che si vedono in produzione
- Proteggere le pagine con il middleware e lasciare le Server Actions aperte: l’azione si chiama anche senza passare dalla pagina.
- Mettere la query dell’utente nel middleware: il tempo di risposta di ogni pagina dipende dal database.
- Dimenticare
/logintra i percorsi pubblici: il redirect punta a una pagina che redirige. - Trattare il ruolo scritto nel cookie come definitivo: il cookie è del client finché non lo verifichi.
Un limite da dichiarare
Il middleware non revoca una sessione già emessa. Un token valido resta valido fino alla scadenza, anche dopo un logout o un cambio di password, a meno che la pagina non consulti una lista di revoca. Tieni la scadenza corta e la revoca nel punto che legge il dato.
I blocchi di accesso già pronti sono confrontati in i migliori blocchi di autenticazione; il confine tra server e client component è spiegato in server component che importa un hook. Il confronto 21st.dev vs shadcnblocks aiuta a scegliere da dove copiare il form.
Domande frequenti
Il middleware basta per proteggere una pagina?
No. Filtra le richieste senza cookie, ma la verifica del token e del permesso deve avvenire nella pagina o nell’azione che legge il dato.
Perché il middleware non può leggere il database?
Perché gira su un runtime leggero, senza driver Node. Una query lì dentro rallenta ogni richiesta e spesso non è nemmeno disponibile.
Come evito un loop di redirect sul login?
Inserisci il login e le altre pagine pubbliche in una lista esclusa, controllata prima del redirect.
Devo proteggere anche le Server Actions?
Sì. Un’azione è un endpoint POST: chi la chiama non passa dal middleware della pagina, quindi ripeti il controllo dentro l’azione.
Middleware o proxy in Next.js 16?
Sono la stessa cosa: il file è stato rinominato proxy.ts, la funzione e il matcher non cambiano.