{"id":64,"date":"2026-06-11T20:43:48","date_gmt":"2026-06-11T20:43:48","guid":{"rendered":"https:\/\/shattered.io\/it\/2026\/06\/11\/autenticazione-jwt-nodejs\/"},"modified":"2026-06-11T20:45:03","modified_gmt":"2026-06-11T20:45:03","slug":"autenticazione-jwt-nodejs","status":"publish","type":"post","link":"https:\/\/shattered.io\/it\/2026\/06\/11\/autenticazione-jwt-nodejs\/","title":{"rendered":"Autenticazione JWT in Node.js: 12 Step [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">L&#8217;<strong>autenticazione JWT<\/strong> \u00e8 diventata lo standard de facto per proteggere le API REST scritte in Node.js. Un token firmato viaggia in ogni richiesta, il server lo verifica senza interrogare un database di sessioni e l&#8217;intera architettura resta stateless. Comodo, veloce, scalabile. E anche pieno di trappole: secreti deboli, algoritmo <code>none<\/code>, token che non scadono mai, refresh token salvati in <code>localStorage<\/code> e pronti per essere rubati da un attacco XSS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Questo tutorial costruisce da zero un sistema completo di <strong>autenticazione JWT<\/strong> in Node.js, con Express 5, registrazione, login, access token e refresh token, rotazione dei refresh, revoca al logout, cookie <code>httpOnly<\/code> e middleware di verifica con allowlist degli algoritmi. Tutto il codice \u00e8 pronto per essere copiato. Tempo stimato: circa 45 minuti. Alla fine avrai un&#8217;API REST funzionante e, soprattutto, capirai dove il 90% dei progetti sbaglia.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"perche-lautenticazione-jwt-domina-le-api-node-js-nel-2026\">Perch\u00e9 l&#8217;autenticazione JWT domina le API Node.js nel 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un JSON Web Token (JWT) \u00e8 una stringa firmata crittograficamente che contiene informazioni sull&#8217;utente. Quando un client effettua il login, il server genera un token e glielo restituisce. Da quel momento il client allega il token a ogni richiesta e il server ne verifica la firma. Se la firma \u00e8 valida, l&#8217;utente \u00e8 autenticato. Nessuna ricerca in tabella, nessuno stato condiviso tra i nodi del backend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Questa propriet\u00e0 stateless \u00e8 la ragione del successo del JWT nelle architetture moderne. Un sistema a microservizi con dieci istanze dietro un load balancer non deve sincronizzare le sessioni: ogni servizio verifica il token in autonomia usando una chiave pubblica. La stessa logica vale per le applicazioni mobile, dove i cookie tradizionali sono scomodi, e per i flussi API tra macchine. Il JWT \u00e8 definito nello standard <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519\" target=\"_blank\" rel=\"noopener\">RFC 7519<\/a>, pubblicato dalla IETF, ed \u00e8 oggi supportato da ogni linguaggio e framework rilevante.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il rovescio della medaglia \u00e8 la sicurezza. Un token stateless resta valido fino alla scadenza, anche se l&#8217;utente fa logout o se l&#8217;account viene compromesso. Per questo un&#8217;implementazione seria non usa un solo token a vita lunga, ma combina un access token a vita breve (5-15 minuti) con un refresh token a vita lunga (7-30 giorni) e un meccanismo di rotazione e revoca. Tutto il tutorial ruota attorno a questo schema, che \u00e8 quello raccomandato anche dal progetto <a href=\"https:\/\/owasp.org\/www-project-top-ten\/\" target=\"_blank\" rel=\"noopener\">OWASP Top Ten<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"come-e-fatto-un-json-web-token-header-payload-e-firma\">Come \u00e8 fatto un JSON Web Token: header, payload e firma<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Un JWT \u00e8 composto da tre parti separate da un punto, codificate in Base64URL: <code>header.payload.signature<\/code>. Le prime due parti sono solo codificate, non cifrate, e chiunque pu\u00f2 leggerle. Questo \u00e8 il malinteso pi\u00f9 diffuso: il payload di un JWT non \u00e8 segreto. Non metterci mai password, numeri di carta o dati sensibili. La firma serve a garantire l&#8217;integrit\u00e0, non la riservatezza.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Header<\/strong>: dichiara il tipo di token (<code>JWT<\/code>) e l&#8217;algoritmo di firma, per esempio <code>{\"alg\":\"RS256\",\"typ\":\"JWT\"}<\/code>.<\/li>\n<li><strong>Payload<\/strong>: contiene le <em>claim<\/em>, cio\u00e8 le affermazioni sull&#8217;utente e sul token (identit\u00e0, scadenza, permessi).<\/li>\n<li><strong>Signature<\/strong>: \u00e8 il risultato della firma crittografica di header e payload con la chiave segreta o privata. Solo chi possiede la chiave pu\u00f2 produrla.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Le claim standard sono definite dalla RFC 7519 e ogni libreria seria le gestisce. Conoscerle evita di reinventare la ruota e di introdurre bug. Ecco le claim registrate pi\u00f9 usate in un flusso di autenticazione.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Claim<\/th><th>Nome esteso<\/th><th>Significato<\/th><th>Esempio<\/th><\/tr><\/thead><tbody><tr><td><code>iss<\/code><\/td><td>Issuer<\/td><td>Chi ha emesso il token<\/td><td>api.miosito.it<\/td><\/tr><tr><td><code>sub<\/code><\/td><td>Subject<\/td><td>Identit\u00e0 dell&#8217;utente<\/td><td>user_8a3f<\/td><\/tr><tr><td><code>aud<\/code><\/td><td>Audience<\/td><td>Destinatario previsto<\/td><td>app-mobile<\/td><\/tr><tr><td><code>exp<\/code><\/td><td>Expiration<\/td><td>Timestamp di scadenza<\/td><td>1749650000<\/td><\/tr><tr><td><code>iat<\/code><\/td><td>Issued At<\/td><td>Quando \u00e8 stato emesso<\/td><td>1749649100<\/td><\/tr><tr><td><code>jti<\/code><\/td><td>JWT ID<\/td><td>Identificatore univoco del token<\/td><td>3f9a-&#8230;-c12b<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">La claim <code>jti<\/code> \u00e8 la chiave di volta della revoca: assegnando a ogni refresh token un identificatore univoco possiamo invalidarlo singolarmente senza buttare via tutti gli altri. La useremo nello Step 7. Le claim <code>exp<\/code> e <code>iat<\/code> permettono invece alla libreria di rifiutare automaticamente i token scaduti, senza che tu debba scrivere una riga di logica temporale.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"jwt-contro-sessioni-server-quando-scegliere-i-token\">JWT contro sessioni server: quando scegliere i token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Prima di scrivere codice conviene capire se il JWT \u00e8 davvero la scelta giusta. Le sessioni server tradizionali, con un identificatore opaco salvato in un cookie e lo stato in Redis o nel database, restano un&#8217;ottima opzione per molte applicazioni web monolitiche. La revoca \u00e8 immediata e il cookie non espone alcun dato. Il JWT vince quando lo stato condiviso \u00e8 un problema: API pubbliche, microservizi, app mobile, autenticazione tra server.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Criterio<\/th><th>Sessione server<\/th><th>JWT (access + refresh)<\/th><\/tr><\/thead><tbody><tr><td>Stato sul server<\/td><td>S\u00ec, per ogni sessione<\/td><td>No per l&#8217;access, s\u00ec solo per i refresh<\/td><\/tr><tr><td>Revoca immediata<\/td><td>Banale<\/td><td>Richiede allowlist o jti store<\/td><\/tr><tr><td>Scalabilit\u00e0 orizzontale<\/td><td>Serve store condiviso<\/td><td>Nativa per l&#8217;access token<\/td><\/tr><tr><td>App mobile e API<\/td><td>Scomoda<\/td><td>Ideale<\/td><\/tr><tr><td>Esposizione dati nel client<\/td><td>Nessuna<\/td><td>Payload leggibile in chiaro<\/td><\/tr><tr><td>Complessit\u00e0 implementativa<\/td><td>Bassa<\/td><td>Media o alta<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">La regola pratica: se hai una singola applicazione web e non prevedi client esterni, le sessioni server sono pi\u00f9 semplici e pi\u00f9 sicure per default. Se invece servi pi\u00f9 client eterogenei o scali su molti nodi, il JWT ripaga la complessit\u00e0 aggiuntiva. Questo tutorial adotta lo schema ibrido pi\u00f9 solido, che mantiene i refresh token in uno store revocabile pur lasciando gli access token completamente stateless.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisiti-e-versioni-dei-pacchetti\">Prerequisiti e versioni dei pacchetti<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Il progetto usa Node.js e un piccolo gruppo di librerie consolidate. Le versioni indicate sono quelle stabili al giugno 2026. Fissa le versioni nel tuo <code>package.json<\/code> e aggiorna regolarmente: mantenere le dipendenze aggiornate \u00e8 una delle poche difese reali contro le vulnerabilit\u00e0 delle librerie JWT.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Strumento \/ pacchetto<\/th><th>Versione consigliata<\/th><th>Ruolo nel progetto<\/th><\/tr><\/thead><tbody><tr><td>Node.js<\/td><td>20 LTS o 22 LTS<\/td><td>Runtime JavaScript<\/td><\/tr><tr><td>npm<\/td><td>10 o superiore<\/td><td>Gestore pacchetti<\/td><\/tr><tr><td>express<\/td><td>5.2.x<\/td><td>Framework HTTP<\/td><\/tr><tr><td>jsonwebtoken<\/td><td>9.0.3<\/td><td>Firma e verifica dei JWT<\/td><\/tr><tr><td>jose<\/td><td>6.2.x<\/td><td>Alternativa moderna (JOSE)<\/td><\/tr><tr><td>bcrypt<\/td><td>6.0.0<\/td><td>Hashing delle password<\/td><\/tr><tr><td>cookie-parser<\/td><td>1.4.x<\/td><td>Lettura dei cookie httpOnly<\/td><\/tr><tr><td>dotenv<\/td><td>16.x<\/td><td>Variabili d&#8217;ambiente<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Verifica la versione di Node prima di iniziare. Se usi una versione precedente alla 18, aggiorna: Express 5 e le API crittografiche moderne richiedono un runtime recente.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node -v\n# atteso: v20.x.x oppure v22.x.x\n\nnpm -v\n# atteso: 10.x.x o superiore<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-1-e-2-inizializzare-il-progetto-node-js-ed-express\">Step 1 e 2: inizializzare il progetto Node.js ed Express<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Crea la cartella del progetto, inizializza npm e installa le dipendenze. Usiamo i moduli ECMAScript (<code>\"type\": \"module\"<\/code>) per scrivere <code>import<\/code> moderni invece di <code>require<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir api-jwt &amp;&amp; cd api-jwt\nnpm init -y\nnpm install express@5 jsonwebtoken@9 bcrypt@6 cookie-parser dotenv\nnpm pkg set type=module<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Struttura le cartelle in modo che ogni responsabilit\u00e0 abbia il suo file. Una buona organizzazione fin dall&#8217;inizio rende il codice di autenticazione pi\u00f9 facile da revisionare, e la revisione \u00e8 proprio dove si trovano i bug di sicurezza.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>api-jwt\/\n  keys\/                 # chiavi RS256 (mai nel repository)\n  src\/\n    config.js           # caricamento variabili e chiavi\n    store.js            # utenti e refresh token (in memoria, demo)\n    tokens.js           # firma e verifica dei JWT\n    middleware.js       # protezione delle rotte\n    routes.js           # register, login, refresh, logout\n    server.js           # avvio dell'app Express\n  .env\n  package.json<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Crea il file <code>src\/server.js<\/code> con lo scheletro dell&#8217;applicazione. Aggiungiamo subito <code>cookie-parser<\/code>, che serve per leggere i refresh token salvati nei cookie <code>httpOnly<\/code>, e un middleware JSON per il corpo delle richieste.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/server.js\nimport express from 'express';\nimport cookieParser from 'cookie-parser';\nimport { router } from '.\/routes.js';\n\nconst app = express();\napp.use(express.json());\napp.use(cookieParser());\napp.use('\/auth', router);\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () =&gt; console.log(`API in ascolto sulla porta ${PORT}`));<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-3-generare-le-chiavi-rs256-e-configurare-lambiente\">Step 3: generare le chiavi RS256 e configurare l&#8217;ambiente<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Qui prendiamo la prima decisione di sicurezza importante: l&#8217;algoritmo di firma. <strong>HS256<\/strong> usa un solo segreto condiviso sia per firmare sia per verificare. Va bene per un singolo servizio, ma in un&#8217;architettura distribuita ogni servizio che verifica avrebbe bisogno del segreto, e quindi potrebbe anche firmare token falsi. <strong>RS256<\/strong> risolve il problema: il server di autenticazione firma con una chiave privata, tutti gli altri verificano con la chiave pubblica. La chiave privata non lascia mai il server di auth.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Generiamo una coppia di chiavi RSA a 2048 bit con OpenSSL. La chiave privata firma, la pubblica verifica.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir keys\nopenssl genrsa -out keys\/private.pem 2048\nopenssl rsa -in keys\/private.pem -pubout -out keys\/public.pem\n# aggiungi keys\/ al .gitignore: le chiavi non vanno mai versionate\necho \"keys\/\" &gt;&gt; .gitignore\necho \".env\"  &gt;&gt; .gitignore<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Crea il file <code>.env<\/code> con i parametri di durata e gli identificatori. Tenere questi valori fuori dal codice permette di cambiarli per ambiente (sviluppo, staging, produzione) senza ricompilare nulla.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># .env\nPORT=3000\nJWT_ISSUER=api.miosito.it\nJWT_AUDIENCE=app-web\nACCESS_TTL=15m\nREFRESH_TTL=7d\nNODE_ENV=development<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Il file <code>src\/config.js<\/code> carica le variabili e legge le chiavi dal disco una sola volta all&#8217;avvio. Leggere le chiavi a ogni richiesta sarebbe uno spreco e un rischio.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/config.js\nimport 'dotenv\/config';\nimport { readFileSync } from 'node:fs';\n\nexport const config = {\n  privateKey: readFileSync('keys\/private.pem'),\n  publicKey:  readFileSync('keys\/public.pem'),\n  issuer:   process.env.JWT_ISSUER,\n  audience: process.env.JWT_AUDIENCE,\n  accessTtl:  process.env.ACCESS_TTL  || '15m',\n  refreshTtl: process.env.REFRESH_TTL || '7d',\n  isProd: process.env.NODE_ENV === 'production',\n};<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-4-registrazione-utente-con-hashing-bcrypt\">Step 4: registrazione utente con hashing bcrypt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Le password non si salvano mai in chiaro e non si firmano dentro un JWT. Si trasformano in un hash con un algoritmo lento e salato come bcrypt. Per la demo teniamo gli utenti in memoria, in un file <code>store.js<\/code>. In produzione qui ci sarebbe il tuo database. Se vuoi approfondire il perch\u00e9 di bcrypt e dei suoi parametri, leggi la nostra guida alla <a href=\"\/it\/sicurezza-delle-password\/\">sicurezza delle password<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/store.js\n\/\/ Store in memoria per la demo. In produzione: database reale.\nexport const users = new Map();          \/\/ email -&gt; { id, email, hash }\nexport const refreshStore = new Map();   \/\/ jti   -&gt; { userId, expiresAt }\n\nexport function findUser(email) {\n  return users.get(email);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La rotta di registrazione valida l&#8217;input, controlla che l&#8217;email non esista gi\u00e0, calcola l&#8217;hash della password con un costo di 12 e crea l&#8217;utente. Il costo 12 \u00e8 un buon compromesso tra sicurezza e latenza nel 2026; valori pi\u00f9 alti rallentano il login senza un vantaggio proporzionale per la maggior parte delle applicazioni.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/routes.js (estratto: registrazione)\nimport { Router } from 'express';\nimport bcrypt from 'bcrypt';\nimport { randomUUID } from 'node:crypto';\nimport { users, findUser } from '.\/store.js';\n\nexport const router = Router();\n\nrouter.post('\/register', async (req, res) =&gt; {\n  const { email, password } = req.body;\n  if (!email || !password || password.length &lt; 10) {\n    return res.status(400).json({ error: 'Email valida e password di almeno 10 caratteri' });\n  }\n  if (findUser(email)) {\n    return res.status(409).json({ error: 'Utente gi\u00e0 registrato' });\n  }\n  const hash = await bcrypt.hash(password, 12);\n  const user = { id: randomUUID(), email, hash };\n  users.set(email, user);\n  return res.status(201).json({ id: user.id, email: user.email });\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-5-login-e-generazione-di-access-e-refresh-token\">Step 5: login e generazione di access e refresh token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Centralizziamo la firma dei token in <code>src\/tokens.js<\/code>. L&#8217;access token porta l&#8217;identit\u00e0 e i permessi e dura 15 minuti. Il refresh token porta solo un <code>jti<\/code> univoco, dura 7 giorni e viene registrato nel <code>refreshStore<\/code> in modo da poterlo revocare. Notare l&#8217;uso esplicito di <code>algorithm: 'RS256'<\/code>, di <code>issuer<\/code> e <code>audience<\/code>: sono questi vincoli a rendere la verifica robusta nello step successivo.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/tokens.js\nimport jwt from 'jsonwebtoken';\nimport { randomUUID } from 'node:crypto';\nimport { config } from '.\/config.js';\nimport { refreshStore } from '.\/store.js';\n\nexport function signAccess(user) {\n  return jwt.sign(\n    { email: user.email, role: 'user' },\n    config.privateKey,\n    {\n      algorithm: 'RS256',\n      subject: user.id,\n      issuer: config.issuer,\n      audience: config.audience,\n      expiresIn: config.accessTtl,\n    }\n  );\n}\n\nexport function signRefresh(user) {\n  const jti = randomUUID();\n  const token = jwt.sign({}, config.privateKey, {\n    algorithm: 'RS256',\n    subject: user.id,\n    jwtid: jti,\n    issuer: config.issuer,\n    audience: config.audience,\n    expiresIn: config.refreshTtl,\n  });\n  const ms = 7 * 24 * 60 * 60 * 1000;\n  refreshStore.set(jti, { userId: user.id, expiresAt: Date.now() + ms });\n  return token;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">La rotta di login verifica la password con <code>bcrypt.compare<\/code>, genera la coppia di token e restituisce l&#8217;access token nel corpo JSON mentre invia il refresh token in un cookie <code>httpOnly<\/code>. Importante: rispondi con lo stesso messaggio di errore generico sia quando l&#8217;email non esiste sia quando la password \u00e8 sbagliata, per non rivelare quali email sono registrate.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/routes.js (estratto: login)\nimport bcrypt from 'bcrypt';\nimport { signAccess, signRefresh } from '.\/tokens.js';\nimport { config } from '.\/config.js';\n\nrouter.post('\/login', async (req, res) =&gt; {\n  const { email, password } = req.body;\n  const user = findUser(email);\n  const ok = user &amp;&amp; await bcrypt.compare(password, user.hash);\n  if (!ok) {\n    return res.status(401).json({ error: 'Credenziali non valide' });\n  }\n  const accessToken  = signAccess(user);\n  const refreshToken = signRefresh(user);\n\n  res.cookie('refresh', refreshToken, {\n    httpOnly: true,\n    secure: config.isProd,\n    sameSite: 'strict',\n    path: '\/auth',\n    maxAge: 7 * 24 * 60 * 60 * 1000,\n  });\n  return res.json({ accessToken });\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-6-middleware-di-verifica-con-allowlist-degli-algoritmi\">Step 6: middleware di verifica con allowlist degli algoritmi<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Questo \u00e8 lo step pi\u00f9 importante per la sicurezza. La verifica deve sempre passare un elenco esplicito di algoritmi accettati a <code>jwt.verify<\/code>. Senza questo vincolo, un attaccante pu\u00f2 modificare l&#8217;header del token e impostare <code>alg: none<\/code> oppure passare da RS256 a HS256 (confusione di algoritmo), inducendo il verificatore a trattare la chiave pubblica come segreto HMAC. Sono due classi di vulnerabilit\u00e0 note da anni e ancora frequenti nel codice reale.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/middleware.js\nimport jwt from 'jsonwebtoken';\nimport { config } from '.\/config.js';\n\nexport function requireAuth(req, res, next) {\n  const header = req.headers.authorization || '';\n  const token = header.startsWith('Bearer ') ? header.slice(7) : null;\n  if (!token) {\n    return res.status(401).json({ error: 'Token mancante' });\n  }\n  try {\n    const payload = jwt.verify(token, config.publicKey, {\n      algorithms: ['RS256'],        \/\/ allowlist esplicita: blocca none e HS256\n      issuer: config.issuer,\n      audience: config.audience,\n    });\n    req.user = { id: payload.sub, email: payload.email, role: payload.role };\n    return next();\n  } catch (err) {\n    return res.status(401).json({ error: 'Token non valido o scaduto' });\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ora puoi proteggere qualsiasi rotta semplicemente anteponendo <code>requireAuth<\/code>. La libreria controlla automaticamente firma, scadenza, issuer e audience: se uno solo di questi non torna, la richiesta viene respinta con 401.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/routes.js (estratto: rotta protetta)\nimport { requireAuth } from '.\/middleware.js';\n\nrouter.get('\/me', requireAuth, (req, res) =&gt; {\n  res.json({ id: req.user.id, email: req.user.email, role: req.user.role });\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-7-rotazione-dei-refresh-token-con-archivio-jti\">Step 7: rotazione dei refresh token con archivio jti<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La rotazione dei refresh token \u00e8 oggi una pratica standard. Quando il client usa un refresh token per ottenere un nuovo access token, il server emette anche un nuovo refresh token e invalida immediatamente quello vecchio. Se un attaccante ruba un refresh token e lo usa, alla volta successiva in cui l&#8217;utente legittimo prova a rinnovare scoprir\u00e0 che il suo token \u00e8 stato gi\u00e0 consumato: il furto diventa rilevabile.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/routes.js (estratto: refresh con rotazione)\nimport jwt from 'jsonwebtoken';\nimport { refreshStore, users } from '.\/store.js';\n\nrouter.post('\/refresh', (req, res) =&gt; {\n  const token = req.cookies?.refresh;\n  if (!token) return res.status(401).json({ error: 'Refresh mancante' });\n\n  let payload;\n  try {\n    payload = jwt.verify(token, config.publicKey, {\n      algorithms: ['RS256'],\n      issuer: config.issuer,\n      audience: config.audience,\n    });\n  } catch {\n    return res.status(401).json({ error: 'Refresh non valido' });\n  }\n\n  \/\/ il jti deve esistere ancora nello store: se manca \u00e8 gi\u00e0 stato ruotato o revocato\n  if (!refreshStore.has(payload.jti)) {\n    return res.status(401).json({ error: 'Refresh revocato' });\n  }\n  refreshStore.delete(payload.jti);   \/\/ invalida il vecchio refresh\n\n  const user = [...users.values()].find(u =&gt; u.id === payload.sub);\n  if (!user) return res.status(401).json({ error: 'Utente assente' });\n\n  const accessToken  = signAccess(user);\n  const refreshToken = signRefresh(user);   \/\/ nuovo jti registrato\n  res.cookie('refresh', refreshToken, {\n    httpOnly: true, secure: config.isProd, sameSite: 'strict',\n    path: '\/auth', maxAge: 7 * 24 * 60 * 60 * 1000,\n  });\n  return res.json({ accessToken });\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In produzione il <code>refreshStore<\/code> non vive in memoria ma in Redis o nel database, cos\u00ec sopravvive ai riavvii e funziona su pi\u00f9 nodi. Un campo con scadenza (TTL) in Redis ripulisce automaticamente i token scaduti.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-8-logout-e-revoca-dei-token\">Step 8: logout e revoca dei token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Poich\u00e9 l&#8217;access token \u00e8 stateless, non pu\u00f2 essere revocato direttamente: resta valido fino alla scadenza. Per questo lo teniamo a vita breve. Il logout, invece, agisce sul refresh token, che \u00e8 quello revocabile. Rimuoviamo il suo <code>jti<\/code> dallo store e cancelliamo il cookie. Dopo al massimo 15 minuti, quando l&#8217;access scade, l&#8217;utente \u00e8 del tutto disconnesso.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/routes.js (estratto: logout)\nrouter.post('\/logout', (req, res) =&gt; {\n  const token = req.cookies?.refresh;\n  if (token) {\n    try {\n      const payload = jwt.verify(token, config.publicKey, {\n        algorithms: ['RS256'], issuer: config.issuer, audience: config.audience,\n      });\n      refreshStore.delete(payload.jti);   \/\/ revoca server-side\n    } catch { \/* token gi\u00e0 scaduto o invalido: ignora *\/ }\n  }\n  res.clearCookie('refresh', { path: '\/auth' });\n  return res.status(204).end();\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se ti serve un logout immediato anche per gli access token, per esempio dopo un cambio password o la compromissione di un account, aggiungi una denylist dei <code>jti<\/code> degli access oppure un campo <code>tokenVersion<\/code> nell&#8217;utente da confrontare nel middleware. \u00c8 un costo in termini di stato, ma \u00e8 l&#8217;unico modo di invalidare un access token prima della scadenza.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-9-e-10-cookie-httponly-csrf-e-protezione-xss\">Step 9 e 10: cookie httpOnly, CSRF e protezione XSS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Dove conservare i token nel browser \u00e8 una delle domande pi\u00f9 dibattute e una delle pi\u00f9 sbagliate nella pratica. Salvare i token in <code>localStorage<\/code> \u00e8 comodo ma pericoloso: qualsiasi script malevolo iniettato tramite XSS pu\u00f2 leggerli e rubarli. La raccomandazione attuale, allineata alle linee guida OWASP, \u00e8 tenere il refresh token in un cookie <code>httpOnly<\/code>, <code>Secure<\/code> e <code>SameSite<\/code>, irraggiungibile da JavaScript, e mantenere l&#8217;access token in memoria (una variabile JavaScript), non in storage persistente.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>httpOnly<\/code>: il cookie non \u00e8 leggibile da <code>document.cookie<\/code>, quindi \u00e8 protetto dall&#8217;XSS.<\/li>\n<li><code>Secure<\/code>: il cookie viaggia solo su HTTPS. Per approfondire il ruolo del TLS leggi <a href=\"\/it\/https-e-tls\/\">HTTPS e TLS<\/a>.<\/li>\n<li><code>SameSite=Strict<\/code> o <code>Lax<\/code>: limita l&#8217;invio del cookie alle richieste cross-site, mitigando il CSRF.<\/li>\n<li>Token in memoria: l&#8217;access token sparisce al refresh della pagina, e va riottenuto via <code>\/refresh<\/code>. \u00c8 un comportamento voluto.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Poich\u00e9 usiamo cookie per il refresh, dobbiamo difenderci dal CSRF. <code>SameSite=Strict<\/code> blocca la maggior parte degli attacchi, ma per sicurezza aggiungi un token anti-CSRF sulle rotte sensibili e applica un <em>rate limiting<\/em> a login e refresh per frenare il brute force. Un middleware come <code>express-rate-limit<\/code> basta per partire.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ esempio: rate limiting su login e refresh\nimport rateLimit from 'express-rate-limit';\n\nconst authLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000,  \/\/ 15 minuti\n  max: 20,                   \/\/ max 20 tentativi per IP\n  standardHeaders: true,\n  message: { error: 'Troppi tentativi, riprova pi\u00f9 tardi' },\n});\n\nrouter.post('\/login',   authLimiter, \/* ...handler... *\/);\nrouter.post('\/refresh', authLimiter, \/* ...handler... *\/);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-11-e-12-testare-lapi-con-esempi-di-output\">Step 11 e 12: testare l&#8217;API con esempi di output<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Avvia il server e prova il flusso completo con <code>curl<\/code>. Prima registriamo un utente, poi facciamo login, infine chiamiamo una rotta protetta con l&#8217;access token ottenuto.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node src\/server.js\n# API in ascolto sulla porta 3000\n\n# 1) registrazione\ncurl -s -X POST http:\/\/localhost:3000\/auth\/register \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"email\":\"mario@example.it\",\"password\":\"PasswordSicura2026\"}'\n# {\"id\":\"7b1d...\",\"email\":\"mario@example.it\"}\n\n# 2) login (salva i cookie in cookies.txt)\ncurl -s -c cookies.txt -X POST http:\/\/localhost:3000\/auth\/login \\\n  -H 'Content-Type: application\/json' \\\n  -d '{\"email\":\"mario@example.it\",\"password\":\"PasswordSicura2026\"}'\n# {\"accessToken\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...\"}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Copia l&#8217;access token e chiama la rotta protetta. Poi prova a rinnovarlo usando il cookie salvato.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># 3) rotta protetta\ncurl -s http:\/\/localhost:3000\/auth\/me \\\n  -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiI...'\n# {\"id\":\"7b1d...\",\"email\":\"mario@example.it\",\"role\":\"user\"}\n\n# 4) rinnovo con rotazione (usa il cookie refresh)\ncurl -s -b cookies.txt -c cookies.txt -X POST http:\/\/localhost:3000\/auth\/refresh\n# {\"accessToken\":\"eyJhbGciOiJSUzI1Ni...\"}  (nuovo access + nuovo refresh nel cookie)\n\n# 5) chiamata senza token\ncurl -s http:\/\/localhost:3000\/auth\/me\n# {\"error\":\"Token mancante\"}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se vedi questi output, l&#8217;autenticazione JWT funziona end-to-end: registrazione, login, accesso protetto, rotazione e rifiuto delle richieste non autenticate. Il sistema \u00e8 completo e pronto per essere collegato a un database reale.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"6-errori-comuni-nellautenticazione-jwt-da-evitare\">6 errori comuni nell&#8217;autenticazione JWT da evitare<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La maggior parte delle falle nei sistemi JWT non nasce dalla crittografia, ma da scelte implementative sbagliate. Ecco i sei errori che ritornano pi\u00f9 spesso nelle revisioni di sicurezza.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Non specificare gli algoritmi in verify.<\/strong> Senza <code>algorithms: ['RS256']<\/code> sei esposto agli attacchi <code>alg: none<\/code> e alla confusione di algoritmo. \u00c8 l&#8217;errore numero uno.<\/li>\n<li><strong>Segreti HS256 deboli.<\/strong> Una chiave come <code>secret123<\/code> si rompe a forza bruta in pochi minuti. Se usi HS256, genera almeno 256 bit casuali e usa segreti diversi per access e refresh.<\/li>\n<li><strong>Mettere dati sensibili nel payload.<\/strong> Il payload \u00e8 leggibile da chiunque. Niente password, token di terze parti o dati personali oltre il necessario.<\/li>\n<li><strong>Access token a vita lunga.<\/strong> Un token valido 30 giorni che non puoi revocare \u00e8 una bomba a orologeria. Tienili a 5-15 minuti.<\/li>\n<li><strong>Refresh token in localStorage.<\/strong> Espone i token all&#8217;XSS. Usa sempre cookie <code>httpOnly<\/code>.<\/li>\n<li><strong>Nessuna rotazione n\u00e9 revoca.<\/strong> Senza <code>jti<\/code> store non puoi disconnettere un utente n\u00e9 rilevare un furto di refresh token.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Un settimo errore, pi\u00f9 sottile, riguarda l&#8217;uso del JWT per le sessioni di una semplice app web monolitica: spesso una sessione server tradizionale sarebbe pi\u00f9 sicura e pi\u00f9 semplice. Scegli il JWT quando ti serve davvero la natura stateless, non perch\u00e9 \u00e8 di moda.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"risoluzione-dei-problemi-8-errori-frequenti\">Risoluzione dei problemi: 8 errori frequenti<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Durante l&#8217;implementazione incontrerai quasi certamente uno di questi messaggi. La tabella mappa l&#8217;errore alla causa e alla soluzione.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Errore<\/th><th>Causa probabile<\/th><th>Soluzione<\/th><\/tr><\/thead><tbody><tr><td><code>invalid signature<\/code><\/td><td>Chiave pubblica non corrispondente alla privata<\/td><td>Rigenera la coppia e verifica i percorsi in config.js<\/td><\/tr><tr><td><code>jwt expired<\/code><\/td><td>Access token scaduto (normale dopo 15 min)<\/td><td>Chiama \/refresh per ottenerne uno nuovo<\/td><\/tr><tr><td><code>jwt audience invalid<\/code><\/td><td>aud nel token diverso da quello atteso<\/td><td>Allinea JWT_AUDIENCE tra firma e verifica<\/td><\/tr><tr><td><code>invalid algorithm<\/code><\/td><td>Algoritmo del token fuori dall&#8217;allowlist<\/td><td>Verifica che firma e verify usino RS256<\/td><\/tr><tr><td><code>secretOrPrivateKey must be...<\/code><\/td><td>Chiave non letta o percorso errato<\/td><td>Controlla readFileSync e l&#8217;esistenza dei file in keys\/<\/td><\/tr><tr><td><code>Refresh revocato<\/code><\/td><td>jti gi\u00e0 ruotato o store riavviato<\/td><td>Rifai login; in produzione usa Redis persistente<\/td><\/tr><tr><td>Cookie non inviato<\/td><td>SameSite o dominio\/percorso errati<\/td><td>Verifica path &#8216;\/auth&#8217; e secure in HTTPS<\/td><\/tr><tr><td>CORS blocca le richieste<\/td><td>credentials non abilitati nel browser<\/td><td>Imposta cors con credentials: true e origin esplicito<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Se l&#8217;errore non rientra in questa lista, abilita un log temporaneo del payload decodificato (senza dati sensibili) con <code>jwt.decode(token)<\/code> per vedere claim, scadenza e algoritmo effettivi. Spesso il problema \u00e8 un disallineamento banale tra issuer o audience.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"consigli-avanzati-per-la-produzione\">Consigli avanzati per la produzione<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Il progetto del tutorial \u00e8 solido come base, ma in produzione conviene aggiungere qualche strato in pi\u00f9. Questi accorgimenti distinguono un&#8217;implementazione didattica da una che regge il traffico reale e gli audit di sicurezza.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Rotazione delle chiavi (key rotation).<\/strong> Pubblica pi\u00f9 chiavi tramite un endpoint JWKS e identifica la chiave attiva con la claim <code>kid<\/code> nell&#8217;header. Cos\u00ec puoi ruotare la chiave di firma senza invalidare tutti i token.<\/li>\n<li><strong>Libreria jose per casi avanzati.<\/strong> Per algoritmi a curva ellittica come EdDSA, per JWE (token cifrati) o per la validazione JWKS, la libreria <a href=\"https:\/\/github.com\/panva\/jose\" target=\"_blank\" rel=\"noopener\">jose<\/a> \u00e8 pi\u00f9 completa di jsonwebtoken.<\/li>\n<li><strong>Store persistente per i refresh.<\/strong> Sposta <code>refreshStore<\/code> su Redis con TTL nativo: revoca distribuita, pulizia automatica e resilienza ai riavvii.<\/li>\n<li><strong>Audience multiple.<\/strong> Se servi pi\u00f9 client (web, mobile, partner), emetti token con audience distinte e verifica quella corretta in ogni servizio.<\/li>\n<li><strong>Monitoraggio.<\/strong> Registra i tentativi di refresh con jti gi\u00e0 consumato: sono il segnale pi\u00f9 affidabile di un furto di token in corso.<\/li>\n<li><strong>Header di sicurezza.<\/strong> Aggiungi <code>helmet<\/code> per impostare automaticamente HSTS, X-Content-Type-Options e Content-Security-Policy, riducendo la superficie XSS.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">La firma RS256 si appoggia alla solidit\u00e0 di RSA, una primitiva che condivide le radici matematiche con le <a href=\"\/it\/firme-digitali\/\">firme digitali<\/a> e con le <a href=\"\/it\/funzioni-hash\/\">funzioni hash crittografiche<\/a>. Capire questi mattoni rende molto pi\u00f9 intuitivo il comportamento dei JWT e aiuta a diagnosticare i problemi quando qualcosa non torna.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"jwt-e-lorizzonte-post-quantistico\">JWT e l&#8217;orizzonte post-quantistico<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Una domanda legittima nel 2026: gli algoritmi di firma dei JWT reggeranno l&#8217;avvento dei computer quantistici? RS256 ed EdDSA si basano su problemi matematici che un computer quantistico abbastanza potente potrebbe risolvere con l&#8217;algoritmo di Shor. La minaccia non \u00e8 immediata, ma le organizzazioni che firmano token a lunga durata o che devono garantire la validit\u00e0 nel tempo dovrebbero iniziare a pianificare la migrazione verso algoritmi resistenti ai quanti.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il NIST ha gi\u00e0 standardizzato i primi algoritmi post-quantistici e l&#8217;ecosistema JOSE sta valutando come integrarli nelle firme dei token. Per la maggior parte delle applicazioni, dove un access token vive 15 minuti e un refresh token sette giorni, il rischio quantistico \u00e8 oggi trascurabile: la finestra di validit\u00e0 \u00e8 troppo breve perch\u00e9 un attacco &#8220;raccogli ora, decifra dopo&#8221; abbia senso. Tieni comunque d&#8217;occhio l&#8217;evoluzione delle librerie e mantieni il codice di firma centralizzato in un solo modulo, come abbiamo fatto in <code>tokens.js<\/code>, proprio per rendere indolore una futura migrazione di algoritmo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"domande-frequenti-sullautenticazione-jwt\">Domande frequenti sull&#8217;autenticazione JWT<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"il-jwt-e-cifrato\">Il JWT \u00e8 cifrato?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. Un JWT standard (JWS) \u00e8 firmato, non cifrato. Header e payload sono solo codificati in Base64URL e chiunque pu\u00f2 leggerli. La firma garantisce che il contenuto non sia stato alterato, non che sia segreto. Per cifrare davvero il contenuto serve JWE, una variante meno comune. La regola resta: non mettere dati sensibili nel payload.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"meglio-hs256-o-rs256\">Meglio HS256 o RS256?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Per un singolo servizio, HS256 con un segreto forte (almeno 256 bit casuali) \u00e8 accettabile e pi\u00f9 semplice. Per pi\u00f9 servizi o quando la verifica avviene su nodi diversi da quello che firma, RS256 \u00e8 la scelta corretta: la chiave privata firma e resta sul server di auth, la chiave pubblica verifica ovunque senza poter falsificare token.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"quanto-deve-durare-un-access-token\">Quanto deve durare un access token?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">La raccomandazione corrente \u00e8 tra 5 e 15 minuti. Una durata breve limita il danno se il token viene rubato, perch\u00e9 diventa inutile in fretta. La continuit\u00e0 dell&#8217;esperienza utente \u00e8 garantita dal refresh token, che rinnova l&#8217;access in modo trasparente senza chiedere di nuovo le credenziali.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"dove-conservo-i-token-nel-browser\">Dove conservo i token nel browser?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Refresh token in un cookie <code>httpOnly<\/code>, <code>Secure<\/code> e <code>SameSite<\/code>, irraggiungibile da JavaScript. Access token in memoria, in una variabile dell&#8217;applicazione. Evita <code>localStorage<\/code> e <code>sessionStorage<\/code> per entrambi: sono leggibili da qualsiasi script e quindi vulnerabili all&#8217;XSS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"come-faccio-logout-se-il-jwt-e-stateless\">Come faccio logout se il JWT \u00e8 stateless?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Revochi il refresh token, cancellando il suo <code>jti<\/code> dallo store e rimuovendo il cookie. L&#8217;access token resta valido fino alla scadenza (per questo lo tieni a 15 minuti). Per un logout istantaneo anche degli access token, aggiungi una denylist dei jti o un campo <code>tokenVersion<\/code> da controllare nel middleware.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"jsonwebtoken-o-jose-quale-libreria-uso\">jsonwebtoken o jose: quale libreria uso?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Per la maggior parte delle applicazioni Node.js, <code>jsonwebtoken<\/code> (versione 9.0.3) \u00e8 la scelta pi\u00f9 diretta e documentata. Se ti servono algoritmi a curva ellittica come EdDSA, token cifrati (JWE) o la validazione di endpoint JWKS, passa a <code>jose<\/code> (versione 6.2.x), pi\u00f9 moderna e completa. Le due possono anche convivere nello stesso progetto.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cose-lattacco-alg-none\">Cos&#8217;\u00e8 l&#8217;attacco &#8220;alg: none&#8221;?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u00c8 un attacco classico: l&#8217;aggressore modifica l&#8217;header del token impostando <code>alg: none<\/code>, che nello standard indica un token senza firma. Se il verificatore non controlla l&#8217;algoritmo, accetta il token come valido pur senza firma, permettendo di falsificare qualsiasi identit\u00e0. La difesa \u00e8 sempre la stessa: passare un&#8217;allowlist esplicita di algoritmi a <code>jwt.verify<\/code>, come fatto nello Step 6.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"posso-usare-lo-stesso-schema-per-unapp-mobile\">Posso usare lo stesso schema per un&#8217;app mobile?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">S\u00ec, ed \u00e8 uno dei casi d&#8217;uso ideali del JWT. Su mobile, dove i cookie sono scomodi, conserva il refresh token nello storage sicuro del dispositivo (Keychain su iOS, Keystore su Android) anzich\u00e9 in un cookie, e mantieni l&#8217;access token in memoria. Lo schema di rotazione e revoca resta identico a quello mostrato qui.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"related-coverage\">Related Coverage<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/it\/crittografia-end-to-end-nodejs\/\">Crittografia End-to-End in Node.js: 12 Step<\/a><\/li>\n<li><a href=\"\/it\/sicurezza-delle-password\/\">Sicurezza delle password: lunghezza, hashing e secondo fattore<\/a><\/li>\n<li><a href=\"\/it\/https-e-tls\/\">HTTPS e TLS: come viene protetta una connessione web<\/a><\/li>\n<li><a href=\"\/it\/firme-digitali\/\">Firme digitali: come funzionano e perch\u00e9 le collisioni le minacciano<\/a><\/li>\n<li><a href=\"\/it\/funzioni-hash\/\">Funzioni hash crittografiche: propriet\u00e0, differenze e usi<\/a><\/li>\n<li><a href=\"\/it\/security-hub\/\">Sicurezza online: violazioni, password, HTTPS e phishing<\/a><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Fonti e approfondimenti esterni: lo standard <a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc7519\" target=\"_blank\" rel=\"noopener\">RFC 7519 (JSON Web Token)<\/a>, il debugger e la documentazione su <a href=\"https:\/\/jwt.io\/\" target=\"_blank\" rel=\"noopener\">jwt.io<\/a>, la <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/JSON_Web_Token_for_Java_Cheat_Sheet.html\" target=\"_blank\" rel=\"noopener\">OWASP JWT Cheat Sheet<\/a>, il repository ufficiale di <a href=\"https:\/\/github.com\/auth0\/node-jsonwebtoken\" target=\"_blank\" rel=\"noopener\">jsonwebtoken<\/a> e quello di <a href=\"https:\/\/github.com\/panva\/jose\" target=\"_blank\" rel=\"noopener\">jose<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>L&#8217;autenticazione JWT \u00e8 diventata lo standard de facto per proteggere le API REST scritte in Node.js. Un token firmato viaggia in ogni richiesta, il server lo verifica senza interrogare un\u2026<\/p>\n","protected":false},"author":7,"featured_media":65,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-64","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security"],"_links":{"self":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/64","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/comments?post=64"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/64\/revisions"}],"predecessor-version":[{"id":66,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/posts\/64\/revisions\/66"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media\/65"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/media?parent=64"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/categories?post=64"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/it\/wp-json\/wp\/v2\/tags?post=64"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}