API Web protetta: verificare gli ambiti e i ruoli dell'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 degli utenti che hanno gli ambiti e i ruoli appropriati.
  • App daemon con i ruoli applicazione corretti.

I frammenti di codice in questo articolo vengono estratti dagli esempi di codice seguenti in 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. Uno che accetta direttamente gli ambiti necessari e uno che accetta una chiave per la configurazione.

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

Il frammento di codice seguente mostra 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 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 VerifyUserHasAnyAcceptedScope metodo di estensione in 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 mostra l'utilizzo dell'attributo [RequiredScope] con ambiti hardcoded nel controller. Per usare RequiredScopeAttribute, è necessario:

  • Usare AddMicrosoftIdentityWebApi in Startup.cs, come illustrato in Configurazione del codice
  • o in caso contrario, aggiungere l'oggetto ScopeAuthorizationRequirement ai criteri di autorizzazione, come illustrato nei criteri di autorizzazione.
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 ambiti definiti nella configurazione

Analogamente all'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

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

Che cos'è verificato?

L'attributo e VerifyUserHasAnyAcceptedScope il [RequiredScope] metodo , esegue una procedura simile alla seguente:

  • 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 [Authorize(Roles = "access_as_application")] attributi 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 scegliere uno tra di loro che si adatta ai rispettivi scenari.

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

Verificare i ruoli dell'app nelle API chiamate per conto degli 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 utenti 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 uguale a 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.

Uso 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 elenco di Controllo di accesso (ACL) per controllare i token senza l'attestazioneroles.

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 AllowWebApiToBeAuthorizedByACL proprietà di configurazione su true in appsettings.json o a livello di codice.

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

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

Passaggi successivi

  • Per altre informazioni, creare un'app Web ASP.NET Core che consente di accedere agli utenti nella serie di esercitazioni in più parti seguente

  • Esplorare gli esempi di API Web di Microsoft Identity Platform