API web protetta: verificare ambiti e ruoli app

Questo articolo illustra come è possibile aggiungere l'autorizzazione a un'API Web. Questa protezione garantisce che l'API venga chiamata solo da:

  • Applicazioni per conto di utenti con gli ambiti e i ruoli corretti.
  • App daemon con i ruoli applicazione corretti.

I frammenti di codice di questo articolo sono estratti dai seguenti esempi di codice su GitHub:

Per proteggere un'API Web ASP.NET o ASP.NET Core, è necessario aggiungere l'attributo [Authorize] a uno degli elementi seguenti:

  • Controller stesso, se si vuole che tutte le azioni del controller siano protette
  • Singola azione del controller per l'API
    [Authorize]
    public class TodoListController : Controller
    {
     // ...
    }

Questa protezione, tuttavia, non è sufficiente. Garantisce solo che ASP.NET e ASP.NET Core convalidino il token. L'API deve verificare che il token usato per chiamare l'API venga richiesto con le attestazioni previste. La verifica è necessaria, in particolare, per queste attestazioni:

  • Ambiti, se l'API viene chiamata per conto di un utente.
  • Ruoli app, se l'API può essere chiamata da un'app daemon.

Verificare gli ambiti nelle API chiamate per conto di utenti

Se un'app client chiama l'API per conto di un utente, l'API deve richiedere un token di connessione con ambiti specifici per l'API. Per altre informazioni, vedere Configurazione del codice - Token di connessione.

In ASP.NET Core è possibile usare Microsoft.Identity.Web per verificare gli ambiti in ogni azione del controller. È anche possibile verificarli a livello di controller o per l'intera applicazione.

Verificare gli ambiti in ogni azione del controller

È possibile verificare gli ambiti nell'azione del controller usando l'attributo [RequiredScope]. Questo attributo ha diverse sostituzioni. Una che accetta direttamente gli ambiti necessari e una che accetta una chiave per la configurazione.

Verificare gli ambiti in un'azione del controller con ambiti hardcoded

Il frammento di codice seguente illustra l'utilizzo dell'attributo [RequiredScope] con ambiti hardcoded.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    const string scopeRequiredByApi = "access_as_user";

    // GET: api/values
    [HttpGet]
    [RequiredScope(scopeRequiredByApi)]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificare gli ambiti in un'azione del controller con gli ambiti definiti nella configurazione

È anche possibile dichiarare questi ambiti obbligatori nella configurazione e fare riferimento alla chiave di configurazione:

Ad esempio, in appsettings.json si dispone della configurazione seguente:

{
 "AzureAd" : {
   // more settings
   "Scopes" : "access_as_user access_as_admin"
  }
}

Quindi, farvi riferimento nell'attributo [RequiredScope]:

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    [RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificare gli ambiti in modo condizionale

Esistono casi in cui si desidera verificare gli ambiti in modo condizionale. A tale scopo, è possibile usare il metodo di estensione VerifyUserHasAnyAcceptedScope nel HttpContext.

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
         HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
        // Do the work and return the result.
        // ...
    }
 // ...
}

Verificare gli ambiti a livello del controller

È anche possibile verificare gli ambiti per l'intero controller

Verificare gli ambiti in un controller con ambiti hardcoded

Il frammento di codice seguente illustra l'utilizzo dell'attributo [RequiredScope] con ambiti hardcoded sul controller. Per usare RequiredScopeAttribute, è necessario:

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
    /// <summary>
    /// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
    /// this API.
    /// </summary>
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };

    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}
Verificare gli ambiti in un controller con gli ambiti definiti nella configurazione

Come per l'azione, è anche possibile dichiarare questi ambiti obbligatori nella configurazione e fare riferimento alla chiave di configurazione:

using Microsoft.Identity.Web

[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
    // GET: api/values
    [HttpGet]
    public IEnumerable<TodoItem> Get()
    {
        // Do the work and return the result.
        // ...
    }
 // ...
}

Verificare gli ambiti più a livello globale

L'approccio consigliato consiste nella definizione di ambiti granulari per l'API Web e nella verifica degli ambiti in ogni azione del controller. Tuttavia, è anche possibile verificare gli ambiti a livello dell'applicazione o di un controller. Per informazioni dettagliate, vedere autorizzazione basata su attestazioni nella documentazione di ASP.NET Core.

Che cosa viene verificato?

L'attributo [RequiredScope] e il metodo VerifyUserHasAnyAcceptedScopeconsentono di eseguire questi passaggi:

  • Verificare la presenza di un'attestazione denominata http://schemas.microsoft.com/identity/claims/scope o scp.
  • Verificare che l'attestazione includa un valore contenente l'ambito previsto dall'API.

Verificare i ruoli app nelle API chiamate da app daemon

Se l'API Web viene chiamata da un'app daemon, tale app dovrà richiedere un'autorizzazione dell'applicazione all'API Web. Come illustrato in Esposizione delle autorizzazioni dell'applicazione (ruoli app), l'API espone tali autorizzazioni. Un esempio è il ruolo app access_as_application.

A questo punto è necessario fare in modo che l'API verifichi che il token ricevuto contenga l'attestazione roles e che questa attestazione abbia il valore previsto. Il codice di verifica è simile al codice che verifica le autorizzazioni delegate, tranne per il fatto che l'azione del controller verifica i ruoli invece degli ambiti:

Il frammento di codice seguente mostra come verificare il ruolo applicazione;

using Microsoft.Identity.Web

[Authorize]
public class TodoListController : ApiController
{
    public IEnumerable<TodoItem> Get()
    {
        HttpContext.ValidateAppRole("access_as_application");
        // ...
    }

È invece possibile usare gli attributi [Authorize(Roles = "access_as_application")] nel controller o in un'azione (o una pagina razor).

[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
    // ...
}

L'autorizzazione basata sui ruoli in ASP.NET Core elenca diversi approcci per implementare l'autorizzazione basata sui ruoli. Gli sviluppatori possono sceglierne uno che si adatti ai rispettivi scenari.

Per esempi di lavoro, vedere l'esercitazione incrementale dell'app Web sull'autorizzazione per ruoli e gruppi.

Verificare i ruoli delle app nelle API chiamate per conto di utenti

Gli utenti possono anche usare le attestazioni dei ruoli nei modelli di assegnazione utente, come illustrato in Come aggiungere ruoli dell'app nell'applicazione e riceverli nel token. Se i ruoli sono assegnabili a entrambi, il controllo dei ruoli consentirà alle app di accedere come utenti e agli utenti di accedere come app. Per evitare questa confusione, è consigliabile dichiarare ruoli diversi per gli utenti e le app.

Se sono stati definiti ruoli app con utente/gruppo, l'attestazione dei ruoli può essere verificata anche nell'API insieme agli ambiti. La logica di verifica dei ruoli dell'app in questo scenario rimane la stessa di se l'API viene chiamata dalle app daemon poiché non esiste alcuna differenziazione nell'attestazione del ruolo per utente/gruppo e applicazione.

Accettazione di token solo per app se l'API Web deve essere chiamata solo da app daemon

Se si vuole che l'API Web venga chiamata solo da app daemon, aggiungere la condizione che il token sia un token solo per app quando si convalida il ruolo app.

string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;

Controllando la condizione inversa, l'API potrà essere chiamata solo dalle app che effettuano l'accesso di un utente.

Utilizzo dell'autorizzazione basata su ACL

In alternativa all'autorizzazione basata sui ruoli dell'app, è possibile proteggere l'API Web con un modello di autorizzazione basato su ACL (Access Control List) per token di controllo senza l'attestazione roles.

Se si usa Microsoft.Identity.Web in ASP.NET Core, sarà necessario dichiarare che si sta usando l'autorizzazione basata su ACL; in caso contrario, Microsoft Identity Web genererà un'eccezione quando non sono presenti ruoli né ambiti nelle attestazioni fornite:

System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.

Per evitare questa eccezione, impostare la proprietà di configurazione AllowWebApiToBeAuthorizedByACL su true in appsettings.json o a livello di codice.

{
 "AzureAD"
 {
  // other properties
  "AllowWebApiToBeAuthorizedByACL" : true,
  // other properties
 }
}

Se si imposta AllowWebApiToBeAuthorizedByACL su true, è responsabilità dell'utente garantire il meccanismo ACL.

Passaggi successivi

  • Per ulteriori informazioni, creare un'app Web ASP.NET Core che effettua l’accesso degli utenti nella serie di esercitazioni in più parti seguenti

  • Esplorare gli esempi di API Web di Microsoft Identity Platform