Programmazione

Il pulsante di emergenza: revoca immediata dei token in .NET 10 con Duende IdentityServer

Dario Fadda Aprile 30, 2026

Immagina questo scenario da incubo: il telefono di un cliente bancario viene rubato, l’app mobile è già autenticata, e il ladro ha pieno accesso al suo conto. Il supporto riceve la chiamata disperata. Ogni secondo conta. Quanto tempo ci vuole per revocare quella sessione attiva e mettere al sicuro i fondi?

Se stai usando JWT self-contained standard, la risposta onesta potrebbe essere “fino a un’ora”, a seconda della durata di validità del token. Non è accettabile. Vediamo come i Reference Token ti forniscono un vero pulsante di emergenza per queste situazioni, e come configurarli con Duende IdentityServer in .NET 10.

Il problema dei JWT self-contained

I JWT self-contained sono il cavallo di battaglia dell’autorizzazione moderna. Trasportano tutte le claim di cui un’API ha bisogno direttamente nel token. Nessuna query al database, nessuna chiamata al provider di identità. L’API valida la firma, controlla la scadenza, e il gioco è fatto. È elegante e performante.

Ma questa natura self-contained è un’arma a doppio taglio. Una volta emesso un JWT, il provider di identità non ha più nulla da dire su di esso. Il token è valido fino a quando la claim exp non dice il contrario, tipicamente 5-60 minuti. Se un dispositivo viene rubato, un account compromesso, o una minaccia rilevata, non puoi revocare quel token. Sei costretto ad aspettare che scada.

Per molte applicazioni questo compromesso è accettabile. Per ambienti ad alta sicurezza come banking, sanità o sistemi governativi, è un gap che non puoi permetterti.

Reference Token: premere il pulsante

I Reference Token ribaltano il modello. Invece di incorporare tutte le claim direttamente nel token, IdentityServer memorizza il contenuto del token lato server nel suo persisted grant store e consegna al client un identificatore opaco (un “handle”). Quando un’API riceve questo handle, chiama l’endpoint di introspection di IdentityServer per validare il token e recuperare le claim.

Questo cambia tutto. Poiché i dati del token risiedono sul server, puoi cancellarli in qualsiasi momento. La revoca è immediata. La prossima volta che l’API chiama l’endpoint di introspection, riceve "active": false, e l’accesso viene negato. Niente attese di scadenza, niente token obsoleti in circolazione.

Il compromesso? Ogni chiamata API richiede un round-trip verso l’endpoint di introspection. Per API pubbliche su scala internet, è una preoccupazione. Per servizi interni e ambienti ad alta sicurezza, è un prezzo ragionevole per la capacità di staccare la spina istantaneamente.

Configurare i Reference Token in IdentityServer

Passare a Reference Token per un client richiede una singola riga di configurazione. Quando definisci il client in Duende IdentityServer, imposta la proprietà AccessTokenType:

new Client
{
    ClientId = "banking_app",
    ClientSecrets = { new Secret("secret".Sha256()) },
    AllowedGrantTypes = GrantTypes.Code,

    // Questa è la riga chiave
    AccessTokenType = AccessTokenType.Reference,

    AllowOfflineAccess = true,
    RedirectUris = { "https://banking.example.com/signin-oidc" },
    AllowedScopes = { "openid", "profile", "accounts.read", "transfers.write" }
};

I token emessi per questo client saranno ora handle opachi invece di JWT self-contained.

Configurare l’API per l’introspection

La tua API deve sapere come validare questi token opachi. Invece del (o in aggiunta al) classico JWT validation, configuri l’introspection OAuth 2.0. Prima, definisci un API Resource con un secret:

new ApiResource("banking_api")
{
    Scopes = { "accounts.read", "transfers.write" },
    ApiSecrets = { new Secret("api_secret".Sha256()) }
};

Poi nel Program.cs della tua API, registra l’handler di introspection:

builder.Services.AddAuthentication("token")
    .AddOAuth2Introspection("token", options =>
    {
        options.Authority = "https://identity.banking.example.com";
        options.ClientId = "banking_api";
        options.ClientSecret = "api_secret";
    });

Se devi supportare sia JWT che Reference Token (magari durante una migrazione), puoi registrare entrambi gli handler e usare il forwarding per instradare i token a quello corretto:

builder.Services.AddAuthentication("token")
    .AddJwtBearer("token", options =>
    {
        options.Authority = "https://identity.banking.example.com";
        options.Audience = "banking_api";
        options.TokenValidationParameters.ValidTypes = ["at+jwt"];
        options.ForwardDefaultSelector = Selector.ForwardReferenceToken("introspection");
    })
    .AddOAuth2Introspection("introspection", options =>
    {
        options.Authority = "https://identity.banking.example.com";
        options.ClientId = "banking_api";
        options.ClientSecret = "api_secret";
    });

Revocare un token

Quando quella chiamata disperata arriva, il tuo sistema di supporto (o una pipeline automatica di rilevamento minacce) può revocare il token immediatamente usando l’endpoint di revocation di IdentityServer, che implementa la RFC 7009:

using Duende.IdentityModel.Client;

var client = new HttpClient();
var result = await client.RevokeTokenAsync(new TokenRevocationRequest
{
    Address = "https://identity.banking.example.com/connect/revocation",
    ClientId = "banking_app",
    ClientSecret = "secret",
    Token = stolenAccessToken
});

if (result.IsError)
{
    logger.LogError("Token revocation failed: {Error}", result.Error);
}

Una volta revocato, il token viene rimosso dal persisted grant store. La prossima richiesta di introspection da qualsiasi API confermerà che il token non è più attivo. L’accesso è tagliato.

Non dimenticare: dovresti anche revocare il refresh token dell’utente per impedire al client di ottenere silenziosamente un nuovo access token:

await client.RevokeTokenAsync(new TokenRevocationRequest
{
    Address = "https://identity.banking.example.com/connect/revocation",
    ClientId = "banking_app",
    ClientSecret = "secret",
    Token = refreshToken
});

Nota: sia l’introspection che la revocation emettono eventi di audit che puoi usare per implementare log di audit nei settori regolamentati.

Quando usare i Reference Token

I Reference Token non sono un sostituto universale dei JWT. Brillano in scenari specifici:

  • La revoca immediata è un requisito imprescindibile (banking, sanità, sistemi compliance-driven)
  • Comunicazione service-to-service interna dove il round-trip di introspection è trascurabile
  • Operazioni ad alto rischio dove il beneficio di sicurezza supera il costo in performance

Per API pubbliche su larga scala dove la latenza di revoca è accettabile, i JWT self-contained con breve durata rimangono una scelta solida. Puoi anche mixare i due approcci: Reference Token per client sensibili e JWT per quelli a minor rischio, tutto all’interno dello stesso deployment IdentityServer.

Conclusione

Ogni architettura di sicurezza implica compromessi. I JWT self-contained scambiano la revocabilità per la performance. I Reference Token scambiano la performance per il controllo. Per gli ambienti dove “aspetta che scada” non è una risposta accettabile, i Reference Token con Duende IdentityServer ti forniscono un vero pulsante di emergenza.

L’implementazione è semplice: una proprietà sul client, un handler di introspection sull’API, e una chiamata di revocation quando devi staccare la spina. Quando accadono incidenti di sicurezza — e accadranno — sarai felice di averlo configurato.

Fonte originale: The Emergency Stop Button – Implementing Immediate Token Revocation in .NET 10 — Khalid Abuhakmeh, Duende Software (28 aprile 2026)

💬 Unisciti alla discussione!


Questo è un blog del Fediverso: puoi trovare quindi questo articolo ovunque con @blog@spcnet.it e ogni commento/risposta apparirà qui sotto.

Se vuoi commentare su Il pulsante di emergenza: revoca immediata dei token in .NET 10 con Duende IdentityServer, utilizza la discussione sul Forum.
Condividi la tua esperienza, confrontati con altri professionisti e approfondisci i dettagli tecnici nel nostro 👉 forum community