Vai al contenuto

L’utenticazione Web: confronto tra sessione, JWT, SSO e OAuth 2.0

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:

  1. L’utente accede con nome utente e password tramite un form interattivo.
  2. Il server crea un ID univoco e lo memorizza in un cookie di sessione, solitamente memorizzato in una cartella del webserver.
  3. 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ì: 

  1. Dopo aver eseguito l’accesso, il server crea un JWT con le informazioni dell’utente.
  2. Il JWT viene memorizzato sul browser (solitamente in localStorage o in un cookie).
  3. 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:

  1. L’utente accede a un server SSO centrale.
  2. Il server SSO genera un token.
  3. 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

  1. Codice di autorizzazione: ideale per le app Web con un backend
  2. Implicito: per app mobili e monopagina (meno sicure, in fase di eliminazione)
  3. Credenziali client: per la comunicazione macchina-macchina
  4. Password: quando l’utente si fida veramente dell’app (non consigliato per le app pubbliche)
  5. 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'));