React
Hook per l’autenticazione in SvelteKit: handle, locals e redirect
In SvelteKit l’autenticazione vive in hooks.server.ts: handle legge il cookie, riempie locals e il redirect sta nel load del gruppo protetto.
In sintesi
- hooks.server.ts è l’unico punto che vede ogni richiesta: lì si legge il cookie e si riempie event.locals.
- Il tipo di locals si dichiara in app.d.ts, altrimenti TypeScript vede un oggetto vuoto.
- Il redirect per una pagina protetta va in +layout.server.ts del gruppo, non nel componente.
In SvelteKit l’autenticazione si aggancia a handle in src/hooks.server.ts: la funzione gira su ogni richiesta, legge il cookie di sessione e riempie event.locals, che load e form action leggono senza ripetere la query.
handle vede tutte le richieste
Il file si chiama hooks.server.ts e non ha equivalente lato client: nessun codice del browser può scrivere locals. Il tipo si dichiara in app.d.ts, altrimenti TypeScript vede un oggetto vuoto.
openssl rand -base64 32
// src/hooks.server.ts
import type { Handle } from "@sveltejs/kit";
export const handle: Handle = async ({ event, resolve }) => {
const token = event.cookies.get("session");
event.locals.user = token ? await readSession(token) : null;
return resolve(event);
};
La dichiarazione dei tipi sta in src/app.d.ts e vale per tutto il progetto: senza, ogni lettura di locals.user è un errore di compilazione.
// src/app.d.ts
declare global {
namespace App {
interface Locals {
user: { id: string; role: "admin" | "member" } | null;
}
}
}
export {};
Il controllo sta nel load del gruppo
Il redirect non va nel componente: va in un +layout.server.ts che copre il gruppo di pagine protette, così la regola vive in un file invece che in dieci.
// src/routes/(app)/+layout.server.ts
import { redirect } from "@sveltejs/kit";
export const load = ({ locals, url }) => {
if (!locals.user) redirect(303, `/login?next=${url.pathname}`);
return { user: locals.user };
};
Più hook, in ordine
sequence() compone più funzioni handle: la prima legge il cookie, la seconda registra la richiesta, la terza applica la regola. L’ordine è quello degli argomenti, quindi chi legge locals.user viene dopo chi lo scrive.
| Punto | Cosa vede | A cosa serve |
|---|---|---|
| hooks.server.ts | Ogni richiesta, anche API e file | Leggere il cookie e riempire locals |
| +layout.server.ts | Le richieste del gruppo | Bloccare le pagine con redirect 303 |
| +page.server.ts | Una route | Controllare un permesso sul singolo dato |
| Form action | Il POST | Verificare il ruolo prima di scrivere |
Il limite da mettere in conto
Gli hook non girano per le pagine prerenderizzate: una pagina con export const prerender = true viene costruita una volta e non può controllare la sessione. E handle gira anche per gli asset, quindi va tenuto corto.
Errori che si vedono
- Leggere la sessione nel componente con
onMount: il primo HTML è già uscito senza utente. - Restituire una risposta da
handlesenza chiamareresolve: la pagina non viene mai resa. - Mettere la query del profilo in
handle: gira anche sulla richiesta di un’immagine. - Salvare il token in
localStorage: il server non lo vede e la pagina protetta resta raggiungibile con una richiesta diretta.
Le pagine che l’hook protegge si compongono da blocchi pronti: login e authentication coprono accesso e recupero, e flowbite-svelte fornisce i campi. Il caricamento dei dati è in load server vs universal, la validazione in form actions in SvelteKit, i file di rotta in routing e layout in SvelteKit, il confronto in Svelte vs React.
Domande frequenti
Posso proteggere solo alcune pagine?
Sì, mettendo il controllo in un +layout.server.ts dentro un gruppo di rotte, per esempio (app). Le pagine fuori dal gruppo non passano da quel load e restano pubbliche senza toccare l’hook.
Dove salvo la sessione?
In un cookie httpOnly, secure e sameSite lax, con un identificativo opaco e non i dati dell’utente. Il cookie viaggia su ogni richiesta, quindi deve essere piccolo e firmato.
Perché locals è vuoto in TypeScript?
Perché manca la dichiarazione in src/app.d.ts. Senza l’interfaccia App.Locals il compilatore non conosce i campi che handle scrive e li segnala come assenti.
Serve un hook per l’autorizzazione?
No, la lettura della sessione basta in un hook. Il permesso dipende dalla rotta e dal dato, quindi si controlla nel load o nell’azione che tocca quel dato, dove il contesto è noto.
Come funziona con più hook?
Con sequence(), che li esegue in ordine e passa il risultato del primo resolve al successivo. Senza sequence vince l’ultima esportazione handle, e le altre vengono ignorate.