Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo articolo si implementa l'autorizzazione nelle API Web ASP.NET Core usando Microsoft. Identity.Web. Verranno convalidati gli ambiti (autorizzazioni delegate) e le autorizzazioni dell'app (autorizzazioni dell'applicazione ) per controllare l'accesso alle risorse protette. Gli esempi utilizzano Microsoft Entra ID come provider di identità.
Comprendere i concetti relativi all'autorizzazione
In questa sezione vengono illustrate le differenze principali tra autenticazione e autorizzazione e viene descritto cosa Microsoft.Identity.Web convalida nei token di accesso.
Autenticazione e autorizzazione
| Concetto | Scopo | Result |
|---|---|---|
| Autenticazione | Verificare l'identità | 401 Non autorizzato se ha esito negativo |
| Autorizzazione | Verificare le autorizzazioni | 403 Proibito se non sufficiente |
Cosa viene convalidato
Quando un'API Web riceve un token di accesso, Microsoft. Identity.Web convalida:
- Firma del token : proviene da un'autorità attendibile?
- Destinatari dei token : è destinato a questa API?
- Scadenza del token : è ancora valida?
- Ambiti/Ruoli - L'app client e il soggetto (utente) hanno le giuste autorizzazioni?
Questa guida è incentrata sul numero 4: convalida degli ambiti e delle autorizzazioni dell'app.
Ambiti (autorizzazioni delegate)
Gli ambiti si applicano quando un utente delega l'autorizzazione a un'app per agire per loro conto(ad esempio, un'API Web chiamata per conto di un utente connesso).
| Dettagli | Value |
|---|---|
| Dichiarazione di token |
scp o scope (applicazione client); roles (utente) |
| Valori di esempio |
"access_as_user", "User.Read", "Files.ReadWrite" |
Autorizzazioni per le app (autorizzazioni dell'applicazione)
Le autorizzazioni dell'app si applicano quando un'app chiama l'API Web come se stessa senza contesto utente, ad esempio un daemon o un servizio in background usando le credenziali client.
| Dettagli | Value |
|---|---|
| Dichiarazione del token | roles |
| Valori di esempio |
"Mail.Read.All", "User.Read.All" |
Convalidare gli ambiti con RequiredScope
L'attributo RequiredScope verifica che il token di accesso contenga almeno uno degli ambiti specificati. Usare questo attributo quando l'API gestisce solo le richieste delegate dall'utente.
Configurare la convalida dell'ambito
Seguire questa procedura per abilitare la convalida dell'ambito nell'API.
1. Abilitare l'autorizzazione nell'API:
Aggiungere servizi di autenticazione e autorizzazione alla pipeline dell'applicazione:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(); // Required for authorization
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization(); // Must be after UseAuthentication
app.MapControllers();
app.Run();
2. Proteggere controller o azioni:
Applicare gli [Authorize] attributi e [RequiredScope] al controller o alle singole azioni:
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Only accessible if token has "access_as_user" scope
return Ok(new[] { "Todo 1", "Todo 2" });
}
}
Applicare i modelli di ambito
Scegliere il modello più adatto alla gestione degli ambiti nell'applicazione.
Modello 1: Ambiti codificati in modo rigido
Usare questo modello quando gli ambiti sono fissi e noti in fase di sviluppo.
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
// All actions require "access_as_user" scope
}
Per accettare uno qualsiasi di più ambiti, elencarli come parametri:
[Authorize]
[RequiredScope("read", "write", "admin")]
public class TodoListController : ControllerBase
{
// Token must have "read" OR "write" OR "admin"
}
Modello 2: Ambiti dalla configurazione
Usare questo modello quando gli ambiti devono essere configurabili per ogni ambiente. Definire gli ambiti nel file di configurazione:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user read write"
}
}
Fare riferimento alla chiave di configurazione nel controller:
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : ControllerBase
{
// Scopes read from configuration
}
Questo approccio consente di modificare gli ambiti senza ricompilare.
Modello 3: Ambiti a livello di azione
Usare questo modello quando diverse azioni richiedono autorizzazioni diverse. Applicare [RequiredScope] ai singoli metodi di azione:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[RequiredScope("read")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[RequiredScope("write")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
// Only tokens with "write" scope can create
return CreatedAtAction(nameof(GetTodos), todo);
}
[HttpDelete("{id}")]
[RequiredScope("admin")]
public IActionResult DeleteTodo(int id)
{
// Only tokens with "admin" scope can delete
return NoContent();
}
}
Informazioni sul flusso di convalida
Quando arriva una richiesta, il middleware lo elabora nell'ordine seguente:
- ASP.NET Core middleware di autenticazione convalida il token
-
RequiredScopel'attributo verifica la presenza dell'attestazionescposcope - Se il token contiene almeno un ambito corrispondente, la richiesta procede.
- Se non viene trovato alcun ambito corrispondente, l'API restituisce una risposta 403 Accesso negato.
L'esempio seguente mostra una risposta di errore tipica:
{
"error": "insufficient_scope",
"error_description": "The token does not have the required scope 'access_as_user'."
}
Convalidare le autorizzazioni dell'app con RequiredScopeOrAppPermission
L'attributo RequiredScopeOrAppPermission convalida gli ambiti (delegati) o le autorizzazioni dell'app (applicazione). Usare questo attributo quando l'API serve app delegate dall'utente e app daemon/service dallo stesso endpoint.
Se l'API gestisce solo le richieste delegate dall'utente, usare RequiredScope invece .
Configurare la convalida dell'ambito o dell'autorizzazione dell'app
Applicare l'attributo per accettare uno dei due tipi di token:
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScopeOrAppPermission(
AcceptedScope = new[] { "access_as_user" },
AcceptedAppPermission = new[] { "TodoList.ReadWrite.All" }
)]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Accessible with EITHER:
// - User-delegated token with "access_as_user" scope, OR
// - App-only token with "TodoList.ReadWrite.All" app permission
return Ok(todos);
}
}
Configurare le autorizzazioni dell'app dalle impostazioni
Archiviare gli ambiti e le autorizzazioni dell'app nella configurazione per modificarli senza ricompilare.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user",
"AppPermissions": "TodoList.ReadWrite.All TodoList.Admin"
}
}
Fare riferimento alle chiavi di configurazione nel controller:
[Authorize]
[RequiredScopeOrAppPermission(
RequiredScopesConfigurationKey = "AzureAd:Scopes",
RequiredAppPermissionsConfigurationKey = "AzureAd:AppPermissions"
)]
public class TodoListController : ControllerBase
{
// Scopes and app permissions from configuration
}
Confrontare le differenze di dichiarazione del token
La tabella seguente illustra come le dichiarazioni differiscono tra i token con delega utente e solo per app.
| Tipo di token | Richiesta di rimborso | Valore di esempio |
|---|---|---|
| Delegata dall'utente |
scp oppure scope |
"access_as_user User.Read" |
| Solo app | roles |
["TodoList.ReadWrite.All"] |
L'esempio seguente mostra un token delegato dall'utente:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"scp": "access_as_user",
"sub": "user-object-id",
...
}
L'esempio seguente mostra un token solo per app:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"roles": ["TodoList.ReadWrite.All"],
"sub": "app-object-id",
...
}
Creare criteri di autorizzazione
Per scenari di autorizzazione complessi, usare ASP.NET Core criteri di autorizzazione. I criteri consentono di centralizzare le regole, combinare più requisiti e scrivere logica di autorizzazione testabile.
| Benefit | Descrizione |
|---|---|
| Logica centralizzata | Definire le regole di autorizzazione una sola volta, riutilizzare ovunque |
| Componibile | Combinare più requisiti (ambiti e attestazioni + logica personalizzata) |
| Verificabile | Logica di autorizzazione unit test più semplice |
| Flessibile | Requisiti personalizzati oltre la convalida dell'ambito |
Modello 1: Definire un criterio con RequireScope
Definire politiche denominate che richiedono ambiti specifici, quindi riferirle nei controller.
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TodoReadPolicy", policyBuilder =>
{
policyBuilder.RequireScope("read", "access_as_user");
});
options.AddPolicy("TodoWritePolicy", policyBuilder =>
{
policyBuilder.RequireScope("write", "admin");
});
});
var app = builder.Build();
Applicare i criteri alle azioni del controller:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "TodoReadPolicy")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[Authorize(Policy = "TodoWritePolicy")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
return CreatedAtAction(nameof(GetTodos), todo);
}
}
Modello 2: Definire un criterio con ScopeAuthorizationRequirement
Usare ScopeAuthorizationRequirement per requisiti di ambito più espliciti:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CustomPolicy", policyBuilder =>
{
policyBuilder.AddRequirements(
new ScopeAuthorizationRequirement(new[] { "access_as_user" })
);
});
});
Modello 3: Impostare un criterio predefinito
Impostare automaticamente un criterio predefinito che si applica a tutti gli [Authorize] attributi:
builder.Services.AddAuthorization(options =>
{
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireScope("access_as_user")
.Build();
options.DefaultPolicy = defaultPolicy;
});
Ogni [Authorize] attributo richiede ora l'ambito access_as_user :
[Authorize] // Automatically requires "access_as_user" scope
public class TodoListController : ControllerBase
{
// All actions protected by default policy
}
Modello 4: Combinare più requisiti
Combinare i requisiti di ambito, ruolo e autenticazione in un singolo criterio:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policyBuilder =>
{
policyBuilder.RequireScope("admin");
policyBuilder.RequireRole("Admin"); // Also check role claim
policyBuilder.RequireAuthenticatedUser();
});
});
Modello 5: Creare una policy dalla configurazione
Caricare gli scopi dalla configurazione per mantenere le policy specifiche per l'ambiente.
var requiredScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ');
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiAccessPolicy", policyBuilder =>
{
if (requiredScopes != null)
{
policyBuilder.RequireScope(requiredScopes);
}
});
});
Filtrare le richieste in base al tenant
Limitare l'accesso API ai token da specifici tenant di Microsoft Entra. Ciò è utile quando l'API multi-tenant deve accettare solo le richieste provenienti dai tenant dei clienti approvati.
Limitare l'accesso ai tenant consentiti
Definire un criterio che controlla l'attestazione ID tenant in base a un elenco di elementi consentiti:
builder.Services.AddAuthorization(options =>
{
string[] allowedTenants =
{
"14c2f153-90a7-4689-9db7-9543bf084dad", // Contoso tenant
"af8cc1a0-d2aa-4ca7-b829-00d361edb652", // Fabrikam tenant
"979f4440-75dc-4664-b2e1-2cafa0ac67d1" // Northwind tenant
};
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
// Apply to all endpoints by default
options.DefaultPolicy = options.GetPolicy("AllowedTenantsOnly");
});
Configurare il filtro dei tenant dalle impostazioni
Archiviare gli ID tenant consentiti nella configurazione per gestirli senza modifiche al codice.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "your-api-client-id",
"AllowedTenants": [
"14c2f153-90a7-4689-9db7-9543bf084dad",
"af8cc1a0-d2aa-4ca7-b829-00d361edb652"
]
}
}
Leggere l'elenco dei tenant e creare la politica all'avvio:
var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants")
.Get<string[]>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants ?? Array.Empty<string>()
);
});
});
Combinare gli ambiti con il filtraggio dei tenant
Creare criteri che richiedono sia un ambito valido che un tenant approvato:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("SecureApiAccess", policyBuilder =>
{
// Require specific scope
policyBuilder.RequireScope("access_as_user");
// AND require specific tenant
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
});
Seguire le migliori pratiche
Applicare queste raccomandazioni per creare logica di autorizzazione sicura e gestibile.
Cose da fare
1. Associa sempre [Authorize] alla convalida dell'ambito:
[Authorize] // Authentication
[RequiredScope("access_as_user")] // Authorization
public class MyController : ControllerBase { }
2. Usare la configurazione per ambiti specifici dell'ambiente:
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
3. Applicare privilegi minimi:
[HttpGet]
[RequiredScope("read")] // Only read permission needed
[HttpPost]
[RequiredScope("write")] // Write permission for modifications
4. Usare i criteri per l'autorizzazione complessa:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
{
policy.RequireScope("admin");
policy.RequireClaim("department", "IT");
});
});
5. Abilitare risposte di errore dettagliate nello sviluppo:
if (builder.Environment.IsDevelopment())
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}
Cose da non fare
1. Non ignorare [Authorize] quando si usa RequiredScope:
// Wrong - RequiredScope won't work without [Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
// Correct
[Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
2. Non utilizzare gli ID tenant codificati in modo rigido nell'ambiente di produzione:
// Wrong
policyBuilder.RequireClaim("tid", "14c2f153-90a7-4689-9db7-9543bf084dad");
// Better - use configuration
var tenants = Configuration.GetSection("AllowedTenants").Get<string[]>();
policyBuilder.RequireClaim("tid", tenants);
3. Non confondere gli ambiti con i ruoli:
// Wrong - This checks roles claim, not scopes
[RequiredScope("Admin")] // "Admin" is typically a role, not a scope
// Correct
[RequiredScope("access_as_user")] // Scope
[Authorize(Roles = "Admin")] // Role
4. Non esporre informazioni sull'ambito sensibile nei messaggi di errore di produzione:
Configurare i livelli di registrazione e la gestione degli errori appropriati per gli ambienti di produzione.
Risolvere i problemi di autorizzazione
Usare le indicazioni seguenti per diagnosticare i problemi di autorizzazione comuni.
403 Accesso vietato - Ambito mancante
Errore: L'API restituisce 403 anche con un token valido.
Diagnosi:
- Decodificare il token in jwt.ms.
- Controllare l'attestazione
scposcope. - Verificare che il valore corrisponda all'attributo
RequiredScope.
Soluzione:
- Assicurarsi che l'app client richieda l'ambito corretto durante l'acquisizione del token.
- Verificare che l'ambito sia esposto nella registrazione dell'applicazione API in Microsoft Entra.
- Concedere il consenso amministratore, se necessario.
RequiredScope non funziona
Sintomo: L'attributo sembra essere ignorato.
Controllare:
- L'attributo
[Authorize]è stato aggiunto? - Viene
app.UseAuthorization()chiamato dopoapp.UseAuthentication()? - È
services.AddAuthorization()registrato?
Chiave di configurazione non trovata
Errore: La convalida dell'ambito fallisce silenziosamente.
Controllare:
{
"AzureAd": {
"Scopes": "access_as_user" // Matches RequiredScopesConfigurationKey
}
}
Verificare che il percorso di configurazione corrisponda esattamente.