Nell’attuale panorama informatico, esistono vari meccanismi di autenticazione, che consentono di sviluppare le cosidette aree ad accesso riservato, tanto care alle applicazioni web. Comprendere i meccanismi che stanno alla base delle varie autenticazioni è fondamentale sia per gli sviluppatori che per le aziende. In questo articolo vedremo le autenticazioni basate su:
- Sessione: ottimo per applicazioni semplici, renderizzate dal server
- JWT: ideale per architetture moderne e stateless e app mobili
- Single Sign-On (SSO): perfetto per ambienti aziendali con più servizi correlati
- OAuth 2.0: la scelta ideale per integrazioni di terze parti e accesso API
Quando scegliamo un metodo di autenticazione, dobbiamo per forza di cose considerare l’architettura dell’applicazione che stiamo creando, la base di utenti, i requisiti di sicurezza e le esigenze di scalabilità. La scelta migliore spesso dipende dal caso d’uso specifico e potrebbe persino comportare una combinazione di questi metodi.
1. Autenticazione basata sulla Sessione: l’approccio classico
Hai presente quando ricevi il braccialetto all’ingresso di un concerto? Una volta dentro, puoi accedere a tutto senza dover mostrare di nuovo il tuo documento d’identità. Questa autenticazione, presente da decenni, funziona proprio cosi. Il funzionamento è abbastanza banale:
- L’utente accede con nome utente e password tramite un form interattivo.
- Il server crea un ID univoco e lo memorizza in un cookie di sessione, solitamente memorizzato in una cartella del webserver.
- Il browser invia questo cookie a ogni richiesta, in modo tale da dimostrare che sei sempre tu.
Pro:
- Semplice da implementare
- Il server ha il controllo completo sulle sessioni
Contro:
- Non è l’ideale per le app mobili
- Può richiedere molte risorse per i server
Esempio pratico
Vediamo come implementare l’autenticazione basata sulla sessione utilizzando Express.js:
const express = require('express');
const session = require('express-session');
const app = express();
app.use(session({
secret: 'chiave-segreta',
resave: false,
saveUninitialized: true,
cookie: { secure: true, maxAge: 24 * 60 * 60 * 1000 } // 24 hours
}));
app.post('/login', (req, res) => {
// Autenticazione utente
req.session.userId = user.id;
res.send('Bentornato!');
});
app.get('/dashboard', (req, res) => {
if (req.session.userId) {
res.send('La tua dashboard');
} else {
res.send('Accesso non consentito');
}
});
app.listen(3000);
2. JWT (JSON Web Token): la soluzione stateless
JWT è come a un passaporto digitale. Contiene tutte le informazioni importanti dell’utente che può utilizzarlo in diversi “paesi” (servizi) senza dover effettuare ogni volta il check-in con il tuo paese di origine. Schematicamente funziona così:
- Dopo aver eseguito l’accesso, il server crea un JWT con le informazioni dell’utente.
- Il JWT viene memorizzato sul browser (solitamente in localStorage o in un cookie).
- Ad ogni richiesta si invia il JWT e il server lo verifica.
Il pacchetto JWT ha una struttura ben definita formata da
- Intestazione: il tipo di token e l’algoritmo di hashing utilizzato
- Payload: i dati utente (richieste)
- Firma: assicura che il token non sia stato manomesso
Pro:
- Stateless e scalabile
- Ottimo per app mobili e onepage
- Può contenere informazioni utente, riducendo le query del database
Contro:
- Richiede una gestione attenta per evitare il furto di token
JWT in azione
Ecco un rapido esempio utilizzando Express.js e la libreria jsonwebtoken:
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
// Authenticate user
const token = jwt.sign(
{ userId: user.id, email: user.email },
'your-secret-key',
{ expiresIn: '1h' }
);
res.json({ token });
});
app.get('/dashboard', (req, res) => {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).send('Access denied');
try {
const verified = jwt.verify(token, 'your-secret-key');
res.send('Welcome to your dashboard, ' + verified.email);
} catch (err) {
res.status(400).send('Invalid token');
}
});
3. Single Sign-On (SSO)
Immaginiamo di avere la mitica chiave passepartout degli inservienti delle pulizie di un albergo. SSO si basa esattamente su questo concetto. Il meccanismo di funzionamento è semplice:
- L’utente accede a un server SSO centrale.
- Il server SSO genera un token.
- Questo token consente all’utente di accedere a più siti correlati senza dover effettuare nuovamente l’accesso.
Pro:
- Incredibilmente facile da usare
- Gestione centralizzata degli utenti
Contro:
- Complesso da installare
- Se il server SSO si blocca, influisce su tutti i servizi connessi
4. OAuth 2.0
Immagina di essere in un parcheggio privato con posteggiatore a cui lasciare le chiavi dell’auto evitando inutili attese. OAuth 2.0 è paragonabile a questo concetto: fornisce un accesso limitato alle tue risorse senza necessariamente rivelare a terzi le proprie credenziali o addirittura l’identità. OAuth 2.0 consente ai servizi di terze parti di accedere ai dati degli utenti senza esporre le password. Non è solo per l’autenticazione, ma per l’autorizzazione.
Tipi di concessione OAuth 2.0
- Codice di autorizzazione: ideale per le app Web con un backend
- Implicito: per app mobili e monopagina (meno sicure, in fase di eliminazione)
- Credenziali client: per la comunicazione macchina-macchina
- Password: quando l’utente si fida veramente dell’app (non consigliato per le app pubbliche)
- Aggiorna token: per ottenere un nuovo token di accesso senza riautenticazione
Pro:
- Altamente flessibile e sicuro
- Consente autorizzazioni dettagliate
- Ampiamente adottato dalle principali aziende tecnologiche
Contro:
- Può essere complesso da implementare correttamente
- Richiede attente considerazioni sulla sicurezza
OAuth 2.0 in azione
Ecco un esempio semplificato del flusso del codice di autorizzazione utilizzando Express.js:
const express = require('express'); const axios = require('axios'); const app = express(); app.get('/login', (req, res) => { const authUrl = `https://oauth.example.com/authorize?client_id=your-client-id&redirect_uri=http://localhost:3000/callback&response_type=code&scope=read_user`; res.redirect(authUrl); }); app.get('/callback', async (req, res) => { const { code } = req.query; try { const tokenResponse = await axios.post('https://oauth.example.com/token', { code, client_id: 'your-client-id', client_secret: 'your-client-secret', redirect_uri: 'http://localhost:3000/callback', grant_type: 'authorization_code' }); const { access_token } = tokenResponse.data; // Use the access_token to make API requests res.send('Authentication successful!'); } catch (error) { res.status(500).send('Authentication failed'); } }); app.listen(3000, () => console.log('Server running on port 3000'));