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.
Questa guida illustra come chiamare le API downstream da applicazioni Web ASP.NET Core e OWIN usando Microsoft. Identity.Web. Nelle app Web si acquisiscono i token per conto dell'utente connesso per chiamare le API con autorizzazioni delegate.
Comprendere il flusso del token
Quando un utente accede all'applicazione Web, è possibile chiamare api downstream (Microsoft Graph, servizi Azure o API personalizzate) per loro conto. Microsoft. Identity.Web gestisce l'acquisizione dei token, la memorizzazione nella cache e l'aggiornamento automatico.
Esaminare il flusso del token di autenticazione utente
sequenceDiagram
participant User as User Browser
participant WebApp as Your Web App
participant AzureAD as Microsoft Entra ID
participant API as Downstream API
User->>WebApp: 1. Access page requiring API data
Note over WebApp: User already signed in
WebApp->>AzureAD: 2. Request access token for API<br/>(using user's refresh token)
AzureAD->>AzureAD: 3. Validate & check consent
AzureAD->>WebApp: 4. Return access token
Note over WebApp: Cache token
WebApp->>API: 5. Call API with token
API->>WebApp: 6. Return data
WebApp->>User: 7. Render page with data
Verificare i prerequisiti
Verificare che l'ambiente soddisfi i requisiti seguenti prima di iniziare.
- App Web configurata con l'autenticazione OpenID Connect
- Accesso utente funzionante
- Registrazione dell'app con autorizzazioni API configurate
- Consenso utente ottenuto (o consenso amministratore concesso)
Implementare ASP.NET Core
1. Configurare l'autenticazione e l'acquisizione di token
Aggiungere i servizi di autenticazione e abilitare l'acquisizione di token nel Program.cs file.
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add authentication with explicit scheme
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
2. Configurare appsettings.json
Definisci la registrazione dell'app per Microsoft Entra ID e le impostazioni dell'API downstream in appsettings.json.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "your-client-secret"
}
]
},
"DownstreamApis": {
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ["user.read", "mail.read"]
},
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"]
}
}
}
Importante: Per le app Web che chiamano LE API downstream, sono necessarie credenziali client (certificato o segreto) oltre alla configurazione di accesso.
3. Aggiungere il supporto dell'API downstream
Scegliere una delle opzioni seguenti per registrare le API downstream.
Opzione A: Registrare API denominate
Il codice seguente registra diverse API a valle dalla configurazione.
using Microsoft.Identity.Web;
// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
builder.Configuration.GetSection("DownstreamApis"));
Option B: Usare Microsoft Graph Helper
Il codice seguente registra il client SDK Microsoft Graph dalla configurazione.
// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));
4. Chiamare l'API downstream dal controller
Iniettare IDownstreamApi nel tuo controller e chiamare l'API per conto dell'utente connesso.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<ProfileController> _logger;
public ProfileController(
IDownstreamApi downstreamApi,
ILogger<ProfileController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> Index()
{
try
{
// Call downstream API on behalf of user
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Incremental consent required
// Redirect user to consent page
return Challenge(
new AuthenticationProperties
{
RedirectUri = "/Profile"
},
OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to call downstream API");
return View("Error");
}
}
}
5. Effettuare una chiamata API downstream dalla pagina Razor
IDownstreamApi Inserire nel modello Razor Page e chiamare l'API.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public ProfileModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
try
{
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
catch (MicrosoftIdentityWebChallengeUserException)
{
// Handle incremental consent
// User will be redirected to consent page
throw;
}
}
}
Chiamare Microsoft Graph
Per le chiamate a Microsoft API Graph, usare il GraphServiceClient dedicato.
Installare i pacchetti
Installare il pacchetto Microsoft Graph per Microsoft. Identity.Web.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Configurare il client Graph nel codice di avvio.
// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(options =>
{
options.Scopes = "user.read mail.read";
})
.AddInMemoryTokenCaches();
Chiamare il API Graph
Inserire GraphServiceClient nel controller per chiamare gli endpoint Microsoft Graph.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
[Authorize]
{
private readonly GraphServiceClient _graphClient;
public HomeController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Index()
{
// Get current user's profile
var user = await _graphClient.Me.GetAsync();
// Get user's emails
var messages = await _graphClient.Me.Messages
.GetAsync(config => config.QueryParameters.Top = 10);
return View(new { User = user, Messages = messages });
}
}
Altre informazioni sull'integrazione Microsoft Graph
Chiamare i client dell'SDK di Azure
Per chiamare i servizi di Azure, usare MicrosoftIdentityTokenCredential.
Installare i pacchetti
Installare i pacchetti di Azure SDK necessari.
dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs
Registrare le credenziali del token Microsoft Entra nel codice di avvio.
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();
Accedere ai servizi di Azure
Immettere le credenziali del token e usarle con i client Azure SDK.
using Azure.Storage.Blobs;
using Microsoft.Identity.Web;
public class StorageController : Controller
{
private readonly MicrosoftIdentityTokenCredential _credential;
public StorageController(MicrosoftIdentityTokenCredential credential)
{
_credential = credential;
}
[Authorize]
public async Task<IActionResult> ListBlobs()
{
var blobClient = new BlobServiceClient(
new Uri("https://myaccount.blob.core.windows.net"),
_credential);
var container = blobClient.GetBlobContainerClient("mycontainer");
var blobs = new List<string>();
await foreach (var blob in container.GetBlobsAsync())
{
blobs.Add(blob.Name);
}
return View(blobs);
}
}
Altre informazioni sull'integrazione Azure SDK
Chiamare API personalizzate con IDownstreamApi
Per le API REST personalizzate, IDownstreamApi offre un approccio semplice basato sulla configurazione.
Configurare l'API
Definire le impostazioni dell'API downstream in appsettings.json.
{
"DownstreamApis": {
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"],
"RequestAppToken": false
}
}
}
Inviare richieste GET
Recuperare dati dall'API downstream con parametri di query facoltativi.
// Simple GET
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
// GET with query parameters
var results = await _downstreamApi.GetForUserAsync<SearchResults>(
"MyAPI",
options =>
{
options.RelativePath = "api/search";
options.QueryParameters = new Dictionary<string, string>
{
["query"] = "test",
["limit"] = "10"
};
});
Inviare richieste POST
Creare una nuova risorsa nell'API downstream pubblicando un corpo della richiesta.
var newItem = new CreateItemRequest
{
Name = "New Item",
Description = "Item description"
};
var created = await _downstreamApi.PostForUserAsync<CreateItemRequest, CreatedItem>(
"MyAPI",
newItem,
options => options.RelativePath = "api/items");
Inviare richieste PUT e DELETE
Aggiornare o eliminare risorse nell'API downstream.
// PUT request
var updated = await _downstreamApi.PutForUserAsync<UpdateRequest, UpdatedItem>(
"MyAPI",
updateData,
options => options.RelativePath = "api/items/123");
// DELETE request
await _downstreamApi.DeleteForUserAsync(
"MyAPI",
null,
options => options.RelativePath = "api/items/123");
Altre informazioni sulle chiamate API personalizzate
Usare IAuthorizationHeaderProvider (avanzato)
Per il controllo massimo sulle richieste HTTP, usare IAuthorizationHeaderProvider.
Registrare il client HTTP
Registra un client HTTP nominato per la tua API downstream.
builder.Services.AddHttpClient("MyAPI", client =>
{
client.BaseAddress = new Uri("https://myapi.example.com");
});
Creare richieste HTTP personalizzate
Compilare e inviare richieste HTTP con intestazioni e autorizzazioni personalizzate.
using Microsoft.Identity.Abstractions;
public class CustomApiService
{
private readonly IAuthorizationHeaderProvider _authProvider;
private readonly IHttpClientFactory _httpClientFactory;
public CustomApiService(
IAuthorizationHeaderProvider authProvider,
IHttpClientFactory httpClientFactory)
{
_authProvider = authProvider;
_httpClientFactory = httpClientFactory;
}
public async Task<MyData> GetDataAsync()
{
// Get authorization header
var authHeader = await _authProvider.CreateAuthorizationHeaderForUserAsync(
new[] { "api://my-api-id/access_as_user" });
// Create HTTP request with custom logic
var client = _httpClientFactory.CreateClient("MyAPI");
var request = new HttpRequestMessage(HttpMethod.Get, "api/resource");
request.Headers.Add("Authorization", authHeader);
request.Headers.Add("X-Custom-Header", "custom-value");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<MyData>();
}
}
Altre informazioni sulla logica HTTP personalizzata
Gestire il consenso incrementale e l'accesso condizionale
Quando si chiamano API downstream, l'applicazione potrebbe dover gestire scenari in cui è necessaria l'interazione dell'utente. Questo avviene in tre scenari principali:
- Consenso incrementale : richiesta di autorizzazioni aggiuntive oltre a quanto inizialmente concesso
- Accesso condizionale - Soddisfare i requisiti di sicurezza, ad esempio MFA, conformità dei dispositivi o criteri di posizione
- Rimozione della cache dei token : ripopolamento della cache dei token dopo il riavvio o la scadenza della cache dell'applicazione
Microsoft. Identity.Web offre la gestione automatica di questi scenari con codice minimo necessario.
Comprendere il flusso
Quando Microsoft. Identity.Web rileva che l'interazione dell'utente è necessaria, genera un MicrosoftIdentityWebChallengeUserException. Il framework gestisce automaticamente questa operazione tramite l'attributo [AuthorizeForScopes] o il MicrosoftIdentityConsentAndConditionalAccessHandler servizio (per Blazor), che:
- Reindirizza l'utente a Microsoft Entra ID per il consenso o l'autenticazione
- Mantiene l'URL della richiesta originale
- Restituisce l'utente alla destinazione desiderata dopo aver completato il flusso
- Memorizza nella cache i token appena acquisiti
Verificare i prerequisiti
Per abilitare la gestione automatica del consenso, assicurati che Program.cs includa la seguente configurazione.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("MyAPI", builder.Configuration.GetSection("MyAPI"))
.AddInMemoryTokenCaches();
// For MVC applications - enables the account controller
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
// Ensure routes are mapped
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); // Required for AccountController
Applicare [AuthorizeForScopes] nei controller MVC
L'attributo [AuthorizeForScopes] , impostato su controller o azioni del controller, gestisce MicrosoftIdentityWebChallengeUserException automaticamente sfidando l'utente quando sono necessarie autorizzazioni aggiuntive.
Dichiarare gli ambiti inline
Specificare gli ambiti necessari direttamente nell'attributo .
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public ProfileController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// AuthorizeForScopes automatically handles consent challenges
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
// Different action requires additional scopes
[AuthorizeForScopes(Scopes = new[] { "user.read", "mail.read" })]
public async Task<IActionResult> Emails()
{
var emails = await _downstreamApi.GetForUserAsync<EmailList>(
"GraphAPI",
options => options.RelativePath = "me/messages");
return View(emails);
}
}
Configurare gli scopi negli 'appsettings'
Archiviare gli ambiti in appsettings.json per una migliore manutenibilità:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "common",
"ClientId": "[Your-Client-ID]",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "[Your-Client-Secret]"
}
]
},
"DownstreamApis": {
"TodoList": {
"BaseUrl": "https://localhost:5001",
"Scopes": [ "api://[API-Client-ID]/access_as_user" ]
},
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/Mail.Send" ]
}
}
}
Controller:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
public class TodoListController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public TodoListController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
var todos = await _downstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
return View(todos);
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:GraphAPI:Scopes:0")]
public async Task<IActionResult> EmailTodos()
{
// If user hasn't consented to Mail.Send, they'll be prompted
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage { /* ... */ },
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Index");
}
}
Configurare Microsoft Entra per ID esterno con i flussi utente
Per le applicazioni con ID esterno (B2C) con più flussi utente, specificare il flusso utente nell'attributo .
[Authorize]
public class AccountController : Controller
{
private const string SignUpSignInFlow = "b2c_1_susi";
private const string EditProfileFlow = "b2c_1_edit_profile";
private const string ResetPasswordFlow = "b2c_1_reset";
[AuthorizeForScopes(
ScopeKeySection = "DownstreamApis:TodoList:Scopes:0",
UserFlow = SignUpSignInFlow)]
public async Task<IActionResult> Index()
{
var data = await _downstreamApi.GetForUserAsync<UserData>(
"TodoList",
options => options.RelativePath = "api/data");
return View(data);
}
[AuthorizeForScopes(
Scopes = new[] { "openid", "offline_access" },
UserFlow = EditProfileFlow)]
public async Task<IActionResult> EditProfile()
{
// This triggers the B2C edit profile flow
return RedirectToAction("Index");
}
}
Applicare [AuthorizeForScopes] nelle Razor Pages
Applica [AuthorizeForScopes] alla classe del modello di pagina:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:MyAPI:Scopes:0")]
public class IndexModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public IndexModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
// Automatically handles consent challenges
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
}
Gestire il consenso in Blazor Server
Le applicazioni Blazor Server richiedono la gestione esplicita delle eccezioni usando il MicrosoftIdentityConsentAndConditionalAccessHandler servizio.
Configurare Program.cs
Registrare il gestore di consenso per Blazor Server nel codice di avvio.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApis("TodoList", builder.Configuration.GetSection("DownstreamApis"))
.AddInMemoryTokenCaches();
// Register the consent handler for Blazor
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
Creare il componente Blazor
Eseguire il wrapping delle chiamate API in blocchi try-catch e usare ConsentHandler.HandleException() per gestire le problematiche di consenso.
@page "/todolist"
@using Microsoft.Identity.Web
@using Microsoft.Identity.Abstractions
@using MyApp.Models
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
@inject IDownstreamApi DownstreamApi
<h3>My Todo List</h3>
@if (todos == null)
{
<p><em>Loading...</em></p>
}
else
{
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
}
@code {
private IEnumerable<TodoItem> todos;
protected override async Task OnInitializedAsync()
{
await LoadTodosAsync();
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
private async Task LoadTodosAsync()
{
try
{
todos = await DownstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
}
catch (Exception ex)
{
// Handles MicrosoftIdentityWebChallengeUserException
// and initiates user consent/authentication flow
ConsentHandler.HandleException(ex);
}
}
private async Task AddTodoAsync(string title)
{
try
{
await DownstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoList",
new TodoItem { Title = title },
options => options.RelativePath = "api/todolist");
await LoadTodosAsync();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
}
Gestire manualmente le eccezioni (avanzate)
Se è necessaria una logica del flusso di consenso personalizzata, gestire MicrosoftIdentityWebChallengeUserException in modo esplicito:
[Authorize]
public class AdvancedController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<AdvancedController> _logger;
public AdvancedController(
IDownstreamApi downstreamApi,
ILogger<AdvancedController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> SendEmail()
{
try
{
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage
{
Subject = "Test",
Body = "Test message"
},
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Success");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Log the consent requirement
_logger.LogWarning(
"Consent required for scopes: {Scopes}. Challenging user.",
string.Join(", ", ex.Scopes));
// Custom properties for redirect
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action("SendEmail", "Advanced"),
};
// Add custom state if needed
properties.Items["consent_attempt"] = "1";
return Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to send email");
return View("Error");
}
}
}
Gestire gli scenari di accesso condizionale
I criteri di accesso condizionale possono richiedere fattori di autenticazione aggiuntivi. La gestione è identica al consenso incrementale:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:SecureAPI:Scopes:0")]
public class SecureDataController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public SecureDataController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// If conditional access requires MFA, AuthorizeForScopes
// automatically challenges the user
var sensitiveData = await _downstreamApi.GetForUserAsync<SensitiveData>(
"SecureAPI",
options => options.RelativePath = "api/sensitive");
return View(sensitiveData);
}
}
Trigger di accesso condizionale comuni:
- Multi-Factor Authentication (MFA)
- Requisito del dispositivo conforme
- Posizione di rete attendibile
- Accettazione delle condizioni per l'utilizzo
- Requisito di modifica della password
Seguire le migliori pratiche
Applicare queste raccomandazioni quando si implementano il consenso e la gestione dell'accesso condizionale.
Utilizzare [AuthorizeForScopes] - Approccio più semplice per controller MVC e Razor Pages
Archiviare gli ambiti nella configurazione : usare ScopeKeySection = "DownstreamApis:ApiName:Scopes:0" per fare riferimento agli ambiti in appsettings.json
Applica a livello di controller : impostare gli ambiti predefiniti nel controller, eseguire l'override su azioni specifiche
Gestire le eccezioni in Blazor - Eseguire sempre il wrapping delle chiamate API con try-catch e usare ConsentHandler.HandleException()
Consentire di generare nuovamente le eccezioni : se si intercetta MicrosoftIdentityWebChallengeUserException, generarla nuovamente in modo [AuthorizeForScopes] da poterla elaborare
Testare l'accesso condizionale : verificare che l'app gestisca correttamente mfa e altri criteri della CA
Non sopprimere le eccezioni - Intercettare senza rilanciare rompe il flusso di consenso
Non memorizzare nella cache le risposte per un periodo illimitato : i token scadono; progettare per la ripetizione dell'autenticazione
Confrontare le autorizzazioni statiche e il consenso incrementale
Autorizzazioni statiche (consenso amministratore)
Tutte le autorizzazioni vengono richieste durante la registrazione dell'app e concesse da un amministratore tenant:
Vantaggi:
- Gli utenti non visualizzano mai richieste di consenso
- Obbligatorio per le app Microsoft proprietarie
- Esperienza utente più semplice
Svantaggi:
- Richiede il coinvolgimento dell'amministratore tenant
- Privilegi elevati fin dall'inizio
- Meno flessibile per scenari multi-tenant
Configuration:
// Request all pre-approved scopes for Microsoft Graph
var scopes = new[] { "https://graph.microsoft.com/.default" };
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"GraphAPI",
options =>
{
options.RelativePath = "me";
options.Scopes = scopes; // Use .default scope
});
Consenso incrementale (dinamico)
Le autorizzazioni vengono richieste in base alle esigenze durante il runtime:
Vantaggi:
- Maggiore sicurezza (principio dei privilegi minimi)
- Gli utenti acconsentono a ciò che usano effettivamente
- Funziona per le app multi-tenant
Svantaggi:
- Gli utenti possono essere interrotti con richieste di consenso
- Richiede la gestione
MicrosoftIdentityWebChallengeUserException
Raccomandazione: Usare il consenso incrementale per le applicazioni multi-tenant; usare le autorizzazioni statiche per le app aziendali proprietarie in cui è garantito il consenso dell'amministratore
Configurare la memorizzazione nella cache dei token
Microsoft. Identity.Web memorizza nella cache i token per migliorare le prestazioni e ridurre le chiamate a Microsoft Entra.
Usare la cache in memoria (impostazione predefinita)
Aggiungere una cache dei token in memoria per scenari di sviluppo o server singolo.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(); // In-memory cache
Usare per:
- Sviluppo
- Distribuzioni a server singolo
- Base utenti di piccole dimensioni
Limitations:
- Non condiviso tra istanze
- Perso al riavvio dell'app
- L'utilizzo della memoria aumenta con gli utenti
Usare la cache distribuita (scelta consigliata per l'ambiente di produzione)
Configurare una cache distribuita, ad esempio Redis o SQL Server per le distribuzioni di produzione.
// Install: Microsoft.Identity.Web.TokenCache
// Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration["Redis:ConnectionString"];
options.InstanceName = "MyApp_";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
// SQL Server
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration["SqlCache:ConnectionString"];
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
Usare per:
- Distribuzioni multiserver (bilanciamento del carico)
- Scenari di disponibilità elevata
- Base utenti di grandi dimensioni
- Cache persistente tra riavvii
Gestire gli errori di acquisizione dei token
Rilevare le eccezioni comuni
Il codice seguente illustra come intercettare e gestire le eccezioni di acquisizione di token più comuni.
try
{
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// User needs to consent or reauthenticate
_logger.LogWarning($"User consent required: {ex.Message}");
return Challenge(new AuthenticationProperties { RedirectUri = Request.Path });
}
catch (MsalUiRequiredException ex)
{
// User interaction required (sign-in again, MFA, etc.)
_logger.LogWarning($"User interaction required: {ex.Message}");
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
catch (MsalServiceException ex)
{
// Service error (Microsoft Entra ID unavailable, etc.)
_logger.LogError(ex, "Microsoft Entra ID service error");
return StatusCode(503, "Authentication service temporarily unavailable");
}
catch (HttpRequestException ex)
{
// Downstream API unreachable
_logger.LogError(ex, "Downstream API call failed");
return StatusCode(503, "Downstream service unavailable");
}
Implementare una degradazione graduale
Caricare dati facoltativi dalle API downstream ed eseguire il fallback alle impostazioni predefinite quando le chiamate hanno esito negativo.
public async Task<IActionResult> Dashboard()
{
var model = new DashboardModel();
// Try to load optional data from downstream API
try
{
model.EnrichedData = await _downstreamApi.GetForUserAsync<EnrichedData>(
"MyAPI",
options => options.RelativePath = "api/enriched");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load enriched data, using defaults");
model.EnrichedData = new EnrichedData { /* defaults */ };
}
return View(model);
}
Implementare OWIN (framework di .NET)
Per le applicazioni Web basate su OWIN in .NET Framework, seguire questa procedura.
1. Installare i pacchetti
Installare i pacchetti NuGet necessari.
Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb
2. Configurare l'avvio
Configurare l'autenticazione e l'acquisizione di token di Microsoft Entra nella classe di avvio OWIN.
using Microsoft.Identity.Web;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.AddMicrosoftIdentityWebApp(
Configuration,
configSectionName: "AzureAd",
openIdConnectScheme: "OpenIdConnect",
cookieScheme: CookieAuthenticationDefaults.AuthenticationType,
subscribeToOpenIdConnectMiddlewareDiagnosticsEvents: true);
app.EnableTokenAcquisitionToCallDownstreamApi();
app.AddDistributedTokenCaches();
}
}
3. Chiamare l'API downstream
Acquisire un token e chiamare l'API a valle da un controller MVC.
using Microsoft.Identity.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> Index()
{
var downstreamApi = TokenAcquirerFactory.GetDefaultInstance()
.GetTokenAcquirer()
.GetDownstreamApi();
var userData = await downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
}
Note: supporto OWIN presenta alcune differenze rispetto a ASP.NET Core. Per informazioni dettagliate, vedere la documentazione di OWIN .
Seguire le procedure consigliate per la sicurezza
Gestire gli ambiti
Applicare il principio dei privilegi minimi quando si richiedono autorizzazioni API.
Do:
- Richiedere solo gli ambiti di autorizzazione necessari
- Usare il consenso incrementale per le funzionalità avanzate
- Documentare gli ambiti necessari nell'app
Don't:
- Richiedere autorizzazioni non necessarie anticipatamente
- Richiedere autorizzazioni solo amministratore senza fornire una giustificazione
- Si supponga che tutti gli ambiti vengano concessi
Gestire i token in modo sicuro
Seguire queste linee guida per proteggere i token di accesso nell'applicazione.
Do:
- Lasciare Microsoft. Identity.Web gestisce i token
- Usare la cache distribuita nell'ambiente di produzione
- Gestire gli errori di acquisizione dei token normalmente
Don't:
- Archiviare i token manualmente
- Token di accesso ai log
- Inviare token al codice lato client
Gestire gli errori
Implementare una gestione affidabile degli errori per l'autenticazione e gli errori delle chiamate API.
Do:
- Rilevare e gestire le eccezioni di consenso
- Fornire messaggi di errore chiari agli utenti
- Errori di log per il debug
Don't:
- Esporre gli errori del token agli utenti
- Consenti alle chiamate API di fallire senza notifiche visibili per l'utente
- Ignorare le eccezioni di autenticazione
Risolvere i problemi comuni
Esaminare queste soluzioni per individuare gli errori di autenticazione rilevati di frequente.
Problema: "AADSTS65001: l'utente o l'amministratore non ha acconsentito"
Causa: L'utente non ha acconsentito agli ambiti obbligatori.
Soluzione:
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Redirect to consent page
return Challenge(
new AuthenticationProperties { RedirectUri = Request.Path },
OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: "AADSTS50076: Autenticazione a più fattori richiesta"
Causa: L'utente deve completare l'autenticazione a più fattori (MFA).
Soluzione:
catch (MsalUiRequiredException)
{
// Redirect user to sign in with MFA
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: i token non vengono mantenuti tra i riavvii dell'app
Causa: Uso della cache in memoria.
Solution: Passare alla cache distribuita (Redis, SQL Server o Cosmos DB).
Problema: 401 Non autorizzato dall'API downstream
Possibili cause:
- Ambiti non corretti richiesti
- Autorizzazione API non concessa nella registrazione dell'app
- Token scaduto
Soluzione:
- Verificare che gli scopi in appsettings.json soddisfino i requisiti dell'API.
- Verificare che la registrazione dell'app disponga delle autorizzazioni API
- Verificare che i token vengano memorizzati nella cache e aggiornati
Per una diagnostica dettagliata: Vedere La Guida alla registrazione e alla diagnostica per gli ID di correlazione, il debug della cache dei token e i modelli di risoluzione dei problemi completi.
Ottimizzare le prestazioni
Pianificare la strategia di memorizzazione nella cache dei token
Selezionare una strategia di memorizzazione nella cache corrispondente alla topologia di distribuzione.
- Usare la cache distribuita per le distribuzioni multiserver
- Configurare la scadenza della cache appropriata
- Monitorare le prestazioni della cache
Ridurre al minimo le richieste di token
Microsoft. Identity.Web memorizza automaticamente i token nella cache. Entrambe le chiamate nell'esempio seguente riutilizzano lo stesso token memorizzato nella cache.
// Bad: Multiple token acquisitions
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
// Good: Single token, multiple calls (token is cached)
// Both calls use the same cached token
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
Effettuare chiamate API parallele
Chiamare più API downstream contemporaneamente per ridurre la latenza complessiva.
// Call multiple APIs in parallel
var profileTask = _downstreamApi.GetForUserAsync<Profile>(
"API1",
options => options.RelativePath = "profile");
var settingsTask = _downstreamApi.GetForUserAsync<Settings>(
"API2",
options => options.RelativePath = "settings");
await Task.WhenAll(profileTask, settingsTask);
var profile = profileTask.Result;
var settings = settingsTask.Result;
Esplorare il contenuto correlato
Altre indicazioni per gli scenari correlati.