.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.
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.