Condividi tramite


API web protetta: verificare ambiti e ruoli app

Si applica a: Cerchio verde con segno di spunta bianco. Tenant delle risorse Cerchio bianco con simbolo X grigio. Tenant esterni (altre informazioni)

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:

  • Gli ambiti utilizzati se l'API viene chiamata per conto di un utente.
  • I ruoli app nel caso in cui l'API possa 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 accesso con specifici ambiti 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 funzione accetta direttamente gli scope richiesti e un'altra accetta una chiave per la configurazione.

Verificare gli ambiti di un'azione del controller con ambiti codificati in modo fisso

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 di un'azione del controller rispetto agli 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 scopi in un controller con scopi codificati staticamente

Il frammento di codice seguente illustra l'utilizzo dell'attributo [RequiredScope] con scopi 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 su 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:

  • Verifica la presenza di una dichiarazione 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 delle app nelle API chiamate dalle app di servizio

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 dell'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 dell'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 pratici, consulta l'esercitazione incrementale dell'applicazione 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 hai definito ruoli dell'app con utenti/gruppi, le dichiarazioni di ruolo possono anche essere verificate 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, è necessario dichiarare che si sta usando l'autorizzazione basata su ACL; in caso contrario, Microsoft Identity Web genera 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

  • Approfondisci creando un'app Web ASP.NET Core che effettua l'accesso degli utenti nella seguente serie di tutorial a più parti

  • Esplorare gli esempi di API Web di Microsoft Identity Platform