← tutti gli articoli

.NET

JWT e Microsoft Identity: come capisco la differenza

Continuavo a trattare JWT e Microsoft Identity come scelte in competizione. Non lo sono. Uno è la biglietteria che conferma chi sei, l'altro è il braccialetto che lo dimostra su ogni giostra dopo.

JWT e Microsoft Identity: come capisco la differenza

Continuavo a trattare JWT e Microsoft Identity come se fossero scelte in competizione, come se la domanda fosse "dovrei usare Identity o dovrei usare JWT". È la domanda sbagliata. Fanno lavori diversi, e nella maggior parte delle API che costruisco, lavorano insieme invece che l'uno contro l'altro.

La versione del parco divertimenti

Il modo in cui questo finalmente ha avuto senso per me: pensa a costruire un sistema per un parco divertimenti.

Microsoft Identity è il database e la biglietteria. Si occupa di chi sei e di cosa contiene il tuo account, le tabelle per utenti, password con hash, role, e claim. È il pezzo che risponde a "registra un nuovo utente", "controlla se questa password è corretta", o "blocca l'account dopo cinque tentativi di login falliti".

Un JWT è il braccialetto. È solo un formato per un badge firmato. Una volta che la biglietteria verifica chi sei, stampa un braccialetto firmato crittograficamente, la stringa del JWT, e te lo consegna. Indossi quel braccialetto su ogni giostra dopo. L'operatore della giostra non chiama la biglietteria ogni volta, controlla solo che il braccialetto abbia una firma valida e ti lascia passare.

Cosa si traduce nel codice

Un request di login va prima a Identity. Trova l'utente, verifica l'hash della password, e solo allora il token viene creato:

[HttpPost("login")]
public async Task<IActionResult> Login(LoginRequest request)
{
    // Microsoft Identity: the ticket booth
    var user = await _userManager.FindByEmailAsync(request.Email);

    if (user is null || !await _userManager.CheckPasswordAsync(user, request.Password))
    {
        return Unauthorized();
    }

    // JWT: the wristband
    var token = _tokenService.CreateToken(user);
    return Ok(new { accessToken = token });
}

_userManager è Identity, che parla con il database. _tokenService si limita a impacchettare le claim che Identity ha già confermato e a firmarle.

Quel token torna al client, e ogni request successivo lo porta nell'header Authorization invece di username e password. Il middleware che lo controlla non tocca mai Identity:

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateLifetime = true
        };
    });

Firma e scadenza, tutto qui. Nessuna chiamata al database su ogni request successivo.

Da dove veniva la confusione

Penso di averli confusi perché entrambi i termini compaiono nelle stesse conversazioni sull'authentication, quindi è facile presumere che siano due opzioni per lo stesso problema. Non appena li ho visti come due livelli diversi, la biglietteria che conferma chi sei, e il braccialetto che lo dimostra su ogni giostra dopo, la confusione si è chiarita.