Aggiungere l'autenticazione Microsoft Entra ID a un'app .NET Aspire

Questa guida illustra come proteggere un'applicazione distribuita .NET Aspire con Microsoft Entra ID l'autenticazione e l'autorizzazione. Si tratta di:

  1. Front-end di Blazor Server (MyService.Web): accesso utente con OpenID Connect e acquisizione di token
  2. Back-end protetto dell'API (MyService.ApiService): validazione JWT tramite Microsoft.Identity.Web
  3. Flusso end-to-end: Blazor acquisisce i token di accesso e chiama l'API protetta con l'individuazione del servizio Aspire

In questa guida si presuppone che sia stato avviato un progetto Aspire creato usando il comando seguente:

aspire new aspire-starter --name MyService

Prerequisiti

Suggerimento

Nuovo su Aspire? Consulta la panoramica di .NET Aspire.

Comprendere il flusso di lavoro in due fasi

Questa guida segue un approccio in due fasi:

Fase Che succede Result
Fase 1 Aggiungere il codice di autenticazione con valori segnaposto L'app si compila ma non viene eseguita
Fase 2 Effettuare il provisioning delle registrazioni delle applicazioni Microsoft Entra L'app viene eseguita con autenticazione reale

Registrare le app in Microsoft Entra ID

Prima che l'app possa autenticare gli utenti, sono necessarie due registrazioni dell'app in Microsoft Entra:

Registrazione dell'app Scopo Configurazione della chiave
API (MyService.ApiService) Convalida i token di ingresso URI ID applicazione, access_as_user ambito
App Web (MyService.Web) Autentica gli utenti, acquisisce i token URI di reindirizzamento, segreto client, autorizzazioni API

Se sono già state configurate registrazioni per le app, sono necessari questi valori per appsettings.json:

  • TenantId - ID tenant di Microsoft Entra
  • API ClientId - ID applicazione (client) della registrazione dell'app per le API
  • URI ID app per le API : in genere api://<api-client-id> (usato in Audiences e Scopes)
  • ClientId app Web - ID applicazione (client) della registrazione dell'app Web
  • Segreto client (o certificato): credenziali per l'app Web (store nei segreti utente, non appsettings.json)
  • Ambiti — Gli ambiti che l'applicazione web richiede, ad esempio api://<api-client-id>/.default o api://<api-client-id>/access_as_user

Passaggio 1: Registrare l'API

  1. Vai a Interfaccia di amministrazione di Microsoft Entra>Identità>Applicazioni>Registrazioni delle app.
  2. Seleziona Nuova registrazione.
    • Nome:MyService.ApiService
    • Tipi di account supportati: Account solo in questa directory organizzativa (tenant singolo)
    • Selezionare Registrazione.
  3. Passare a Esporre un'API>Aggiungi accanto all'URI ID applicazione.
    • Accettare il valore predefinito (api://<client-id>) o personalizzarlo.
    • Selezionare Aggiungi un ambito:
      • Nome ambito:access_as_user
      • Chi può fornire il consenso: Amministratori e utenti
      • Nome visualizzato del consenso amministratore: Accedere all'API MyService
      • Descrizione del consenso amministratore: Consente all'app di accedere all'API MyService per conto dell'utente connesso.
      • Seleziona Aggiungi ambito.
  4. Copiare l'ID applicazione (client), necessario per entrambi i appsettings.json file.

Per altre informazioni, vedere Avvio rapido: Configurare un'app per esporre un'API Web.

Passaggio 2: Registrare l'app Web

  1. Passare a Registrazioni app>Nuova registrazione.
    • Nome:MyService.Web
    • Tipi di account supportati: Solo gli account in questa directory organizzativa
    • URI di reindirizzamento: Selezionare Web e immettere l'URL dell'app + /signin-oidc
      • Per lo sviluppo locale: https://localhost:7001/signin-oidc (controlla il tuo launchSettings.json per la porta effettiva)
    • Selezionare Registrazione.
  2. Passare a Authentication e > per aggiungere tutti gli URL di sviluppo (da ).
  3. Passare a Certificati e segreti>Segreti client>Nuovo segreto client.
    • Aggiungere una descrizione e una scadenza.
    • Copiare immediatamente il valore del segreto. Non verrà più visualizzato.
  4. Passare a Autorizzazioni> APIAggiungere un'autorizzazione>Api personali.
    • Seleziona MyService.ApiService.
    • Selezionare access_as_user>Aggiungi autorizzazioni.
    • Selezionare Concedi consenso amministratore per [tenant] (o agli utenti viene richiesto al primo utilizzo).
  5. Copiare l'ID applicazione (client) per l'app Web.appsettings.json

Annotazioni

Alcune organizzazioni non consentono segreti client. Per altre alternative, vedere Credenziali del certificato o Autenticazione senza certificato.

Per altre informazioni, vedere Guida introduttiva: Registrare un'applicazione.

Passaggio 3: Aggiornare la configurazione

Dopo aver creato le registrazioni dell'app, aggiornare i appsettings.json file:

API (MyService.ApiService/appsettings.json):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_API_CLIENT_ID",
    "Audiences": ["api://YOUR_API_CLIENT_ID"]
  }
}

App Web (MyService.Web/appsettings.json):

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_WEB_CLIENT_ID",
    "CallbackPath": "/signin-oidc",
    "ClientCredentials": [
      { "SourceType": "ClientSecret" }
    ]
  },
  "WeatherApi": {
    "Scopes": ["api://YOUR_API_CLIENT_ID/.default"]
  }
}

Archiviare il segreto in modo sicuro:

cd MyService.Web
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "YOUR_SECRET_VALUE"
Valore Dove trovare
TenantId Panoramica Interfaccia di amministrazione di Microsoft Entra >> ID tenant
API ClientId ID applicazione Registrazioni applicazioni > MyService.ApiService > Applicazione (cliente)
Web ClientId Registrazioni app > MyService.Web > ID dell'applicazione (client)
Client Secret Creato nel passaggio 2 (copia immediatamente al momento della creazione)

Annotazioni

Il modello di avvio Aspire crea automaticamente una classe WeatherApiClient nel progetto MyService.Web. Questo HttpClient tipizzato viene usato in questa guida per dimostrare di chiamare l'API protetta. Non è necessario creare manualmente questa classe, che fa parte del modello.


Inizia subito

Questa sezione fornisce un riferimento condensato per l'aggiunta dell'autenticazione. Per procedure dettagliate, vedere Parte 1 e Parte 2.

API (MyService.ApiService)

Installare il pacchetto NuGet Microsoft.Identity.Web:

dotnet add package Microsoft.Identity.Web

Aggiungere la configurazione di Microsoft Entra a appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<tenant-id>",
    "ClientId": "<api-client-id>",
    "Audiences": ["api://<api-client-id>"]
  }
}

Registrare l'autenticazione e l'autorizzazione in Program.cs:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
app.MapGet("/weatherforecast", () => { /* ... */ }).RequireAuthorization();

App Web (MyService.Web)

Installare il pacchetto NuGet Microsoft.Identity.Web:

dotnet add package Microsoft.Identity.Web

Aggiungere la configurazione di Microsoft Entra a appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<tenant-id>",
    "ClientId": "<web-client-id>",
    "CallbackPath": "/signin-oidc",
    "ClientCredentials": [{ "SourceType": "ClientSecret" }]
  },
  "WeatherApi": { "Scopes": ["api://<api-client-id>/.default"] }
}

Configurare l'autenticazione, l'acquisizione di token e il client API downstream in Program.cs:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    client.BaseAddress = new("https+http://apiservice"))
    .AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
// ...
app.UseAuthentication();
app.UseAuthorization();
app.MapGroup("/authentication").MapLoginAndLogout();

Acquisisce MicrosoftIdentityMessageHandler e collega automaticamente i token e BlazorAuthenticationChallengeHandler gestisce i problemi di consenso e accesso condizionale.

Importante

Non dimenticare di creare UserInfo.razor per il pulsante di accesso. Per informazioni dettagliate, vedere Aggiungere componenti dell'interfaccia utente Blazor .

Annotazioni

BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions sono inclusi in Microsoft.Identity.Web (v3.3.0+). Non è necessaria alcuna copia di file.


Identificare i file da modificare

La tabella seguente elenca i file modificati in ogni progetto:

Progetto File Changes
ApiService Program.cs Autenticazione JWT Bearer, middleware di autorizzazione
appsettings.json configurazione Microsoft Entra
.csproj Aggiungere Microsoft.Identity.Web
Web Program.cs Autenticazione OIDC, acquisizione di token, BlazorAuthenticationChallengeHandler
appsettings.json Microsoft Entra config, ambiti dell'API a valle
.csproj Aggiungere Microsoft.Identity.Web (v3.3.0+)
Components/UserInfo.razor Interfaccia utente pulsante di accesso (nuovo file)
Components/Layout/MainLayout.razor Includere il componente UserInfo
Components/Routes.razor AuthorizeRouteView per le pagine protette
Pagine che chiamano le API Provare/intercettare con ChallengeHandler

Informazioni sul flusso di autenticazione

Il diagramma seguente mostra come interagiscono il front-end Blazor, Microsoft Entra e l'API protetta:

flowchart LR
  A[User Browser] -->|1 Login OIDC| B[Blazor Server<br/>MyService.Web]
  B -->|2 Redirect| C[Microsoft Entra ID]
  C -->|3 auth code| B
  B -->|4 exchange auth code| C
  C -->|5 tokens| B
  B -->|6 cookie + session| A
  B -->|7 HTTP + Bearer token| D[ASP.NET API<br/>MyService.ApiService<br/>Microsoft.Identity.Web]
  D -->|8 Validate JWT| C
  D -->|9 Weather data| B
  1. L'utente visita l'app Blazor → Non autenticato → vede il pulsante "Login".
  2. Utente seleziona Login → Viene reindirizzato a /authentication/login → Sfida OIDC → Microsoft Entra.
  3. Utente accede → Microsoft Entra reindirizza /signin-oidc → cookie impostato.
  4. L'utente passa alla pagina Meteo → Blazor chiama WeatherApiClient.GetAsync().
  5. MicrosoftIdentityMessageHandler intercetta la richiesta, acquisisce un token dalla cache (o aggiorna automaticamente) e allega l'intestazione Authorization: Bearer <token> .
  6. API riceve la richiesta → Microsoft. Identity.Web convalida l'→ JWT restituisce i dati.
  7. Blazor esegue il rendering dei dati meteo.

Esaminare la struttura della soluzione

Modello di avvio Aspire crea il seguente layout di progetto:

MyService/
├── MyService.AppHost/           # Aspire orchestration
├── MyService.ApiService/        # Protected API (Microsoft.Identity.Web)
├── MyService.Web/               # Blazor Server (Microsoft.Identity.Web)
├── MyService.ServiceDefaults/   # Shared defaults
└── MyService.Tests/             # Tests

Parte 1: Proteggere il back-end dell'API con Microsoft. Identity.Web

Questa sezione configura il progetto API per convalidare i token di connessione JWT rilasciati da Microsoft Entra.

Aggiungere il pacchetto Microsoft.Identity.Web

Eseguire il comando seguente per installare il pacchetto NuGet Microsoft.Identity.Web:

cd MyService.ApiService
dotnet add package Microsoft.Identity.Web

Configurare le impostazioni di Microsoft Entra

Aggiungere la configurazione di Microsoft Entra a MyService.ApiService/appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<your-tenant-id>",
    "ClientId": "<your-api-client-id>",
    "Audiences": [
      "api://<your-api-client-id>"
    ]
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Proprietà chiave:

  • ClientId: ID registrazione dell'app per le API Microsoft Entra
  • TenantId: ID del tenant Microsoft Entra, oppure "organizations" per multi-tenant, oppure "common" per qualsiasi account Microsoft
  • Audiences: destinatari validi del token (in genere l'URI dell'ID dell'app)

Aggiornare Program.cs API

Sostituire il contenuto di MyService.ApiService/Program.cs con il codice seguente per aggiungere l'autenticazione JWT Bearer e proteggere gli endpoint:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

// Add Microsoft.Identity.Web JWT Bearer authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();
builder.Services.AddAuthorization();

var app = builder.Build();

app.UseExceptionHandler();
app.UseAuthentication();
app.UseAuthorization();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

string[] summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild",
    "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];

app.MapGet("/", () =>
    "API service is running. Navigate to /weatherforecast to see sample data.");

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.RequireAuthorization();

app.MapDefaultEndpoints();
app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Modifiche chiave:

  • Registrare l'autenticazione Bearer JWT con AddMicrosoftIdentityWebApi
  • Aggiungere app.UseAuthentication() e app.UseAuthorization() middleware
  • Applicare .RequireAuthorization() agli endpoint protetti

Testare l'API protetta

Verificare che l'API rifiuti le richieste non autenticate e accetti token validi.

Inviare una richiesta senza token:

curl https://localhost:<PORT>/weatherforecast
# Expected: 401 Unauthorized

Inviare una richiesta con un token valido:

curl -H "Authorization: Bearer <TOKEN>" https://localhost:<PORT>/weatherforecast
# Expected: 200 OK with weather data

Parte 2: Configurare il front-end Blazor per l'autenticazione

L'app Blazor Server usa Microsoft. Identity.Web per:

  • Accedi agli utenti con OIDC
  • Acquisire i token di accesso per chiamare l'API
  • Collegare token a richieste HTTP in uscita

Aggiungere il pacchetto Microsoft.Identity.Web

Eseguire il comando seguente per installare il pacchetto NuGet Microsoft.Identity.Web.

cd MyService.Web
dotnet add package Microsoft.Identity.Web

Configurare le impostazioni di Microsoft Entra

Aggiungere gli ambiti di configurazione Microsoft Entra e le API "downstream" a MyService.Web/appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<your-tenant>.onmicrosoft.com",
    "TenantId": "<tenant-guid>",
    "ClientId":  "<web-app-client-id>",
    "CallbackPath": "/signin-oidc",
    "ClientCredentials": [
      {
        "SourceType": "ClientSecret",
        "ClientSecret": "<your-client-secret>"
      }
    ]
  },
  "WeatherApi": {
    "Scopes": [ "api://<api-client-id>/.default" ]
  },
  "Logging": {
    "LogLevel":  {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Dettagli di configurazione:

  • ClientId: ID registrazione app Web (non ID API)
  • ClientCredentials: credenziali per l'app Web per acquisire i token. Supporta più tipi di credenziali. Consulta Panoramica delle credenziali per le opzioni pronte per l'uso in produzione.
  • Scopes: deve corrispondere all'URI ID dell'app dell'API con il suffisso /.default

Avvertimento

Per l'ambiente di produzione, usare certificati o identità gestita anziché segreti client. Per l'approccio consigliato, vedere Autenticazione senza certificati.

Aggiornare il Program.cs dell'applicazione web

Sostituire il contenuto di MyService.Web/Program.cs con il codice seguente per configurare l'autenticazione OIDC, l'acquisizione di token e il client dell'API downstream:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using MyService.Web;
using MyService.Web.Components;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

// Authentication + Microsoft Identity Web
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

builder.Services.AddCascadingAuthenticationState();

// Blazor components
builder.Services.AddRazorComponents().AddInteractiveServerComponents();

// Blazor authentication challenge handler for incremental consent and Conditional Access
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();

builder.Services.AddOutputCache();

// Downstream API client with MicrosoftIdentityMessageHandler
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
    // Aspire service discovery: resolves "apiservice" at runtime
    client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.UseOutputCache();

app.MapStaticAssets();
app.MapRazorComponents<App>()
   .AddInteractiveServerRenderMode();

// Login/Logout endpoints with incremental consent support
app.MapGroup("/authentication").MapLoginAndLogout();

app.MapDefaultEndpoints();
app.Run();

Punti principali:

  • AddMicrosoftIdentityWebApp: configura l'autenticazione OIDC
  • EnableTokenAcquisitionToCallDownstreamApi: abilita l'acquisizione di token per le API downstream
  • AddScoped<BlazorAuthenticationChallengeHandler>: gestisce il consenso incrementale e l'accesso condizionale nel server Blazor
  • AddMicrosoftIdentityMessageHandler: collega automaticamente i token portatori alle richieste HttpClient
  • https+http://apiservice: il rilevamento del servizio Aspire lo risolve nell'URL effettivo dell'API
  • Ordine del middleware: UseAuthentication()UseAuthorization() → endpoint

L'estensione AddMicrosoftIdentityMessageHandler supporta più modelli di configurazione:

Opzione 1: Configurazione da appsettings.json (illustrato in precedenza)

.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));

Opzione 2: Configurazione inline con delegato Action

.AddMicrosoftIdentityMessageHandler(options =>
{
    options.Scopes.Add("api://<api-client-id>/.default");
});

Opzione 3: configurazione per richiesta (senza parametri)

.AddMicrosoftIdentityMessageHandler();

// Then in your service, configure per-request:
var request = new HttpRequestMessage(HttpMethod.Get, "/weatherforecast")
    .WithAuthenticationOptions(options =>
    {
        options.Scopes.Add("api://<api-client-id>/.default");
    });
var response = await _httpClient.SendAsync(request);

Aggiungere componenti dell'interfaccia utente Blazor

Importante

Questo passaggio viene spesso dimenticato. Senza il componente UserInfo, gli utenti non possono accedere.

BlazorAuthenticationChallengeHandler e LoginLogoutEndpointRouteBuilderExtensions sono inclusi in Microsoft.Identity.Web v3.3.0+. Sono disponibili automaticamente dopo aver fatto riferimento al pacchetto. Non è necessaria alcuna copia di file.

Creare MyService.Web/Components/UserInfo.razor:

@using Microsoft.AspNetCore.Components.Authorization

<AuthorizeView>
    <Authorized>
        <span class="nav-item">Hello, @context.User.Identity?.Name</span>
        <form action="/authentication/logout" method="post" class="nav-item">
            <AntiforgeryToken />
            <input type="hidden" name="returnUrl" value="/" />
            <button type="submit" class="btn btn-link nav-link">Logout</button>
        </form>
    </Authorized>
    <NotAuthorized>
        <a href="/authentication/login?returnUrl=/" class="nav-link">Login</a>
    </NotAuthorized>
</AuthorizeView>

Aggiungi al layout: Includi <UserInfo /> nel MainLayout.razor:

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <UserInfo />
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

Aggiornare Routes.razor per AuthorizeRouteView

Sostituire RouteView con AuthorizeRouteView in Components/Routes.razor:

@using Microsoft.AspNetCore.Components.Authorization

<Router AppAssembly="typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
            <NotAuthorized>
                <p>You are not authorized to view this page.</p>
                <a href="/authentication/login">Login</a>
            </NotAuthorized>
        </AuthorizeRouteView>
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
</Router>

Gestire le eccezioni nelle pagine che chiamano le API

Blazor Server richiede la gestione esplicita delle eccezioni per l'accesso condizionale e il consenso. Devi gestire MicrosoftIdentityWebChallengeUserException in ogni pagina che chiama un'API downstream, a meno che l'app non sia pre-autorizzata e richiedi tutti gli ambiti in anticipo in Program.cs.

L'esempio seguente Weather.razor illustra la gestione corretta delle eccezioni:

@page "/weather"
@attribute [Authorize]

@using Microsoft.AspNetCore.Authorization
@using Microsoft.Identity.Web

@inject WeatherApiClient WeatherApi
@inject BlazorAuthenticationChallengeHandler ChallengeHandler

<PageTitle>Weather</PageTitle>

<h1>Weather</h1>

@if (!string.IsNullOrEmpty(errorMessage))
{
    <div class="alert alert-warning">@errorMessage</div>
}
else if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[]? forecasts;
    private string? errorMessage;

    protected override async Task OnInitializedAsync()
    {
        if (!await ChallengeHandler.IsAuthenticatedAsync())
        {
            await ChallengeHandler.ChallengeUserWithConfiguredScopesAsync("WeatherApi:Scopes");
            return;
        }

        try
        {
            forecasts = await WeatherApi.GetWeatherAsync();
        }
        catch (Exception ex)
        {
            // Handle incremental consent / Conditional Access
            if (!await ChallengeHandler.HandleExceptionAsync(ex))
            {
                errorMessage = $"Error loading weather data: {ex.Message}";
            }
        }
    }
}

Il modello funziona come segue:

  1. IsAuthenticatedAsync() controlla se l'utente ha eseguito l'accesso prima di effettuare chiamate API.
  2. HandleExceptionAsync() catches MicrosoftIdentityWebChallengeUserException (in qualità di InnerException).
  3. Se si tratta di un'eccezione di sfida, l'utente viene reindirizzato per ripetere l'autenticazione con le attestazioni o gli ambiti necessari.
  4. Se non si tratta di un'eccezione di sfida, HandleExceptionAsync restituisce false così puoi gestire l'errore da solo.

Archiviare il segreto client nei segreti utente

Usare .NET Secret Manager per archiviare il segreto client in modo sicuro durante lo sviluppo.

Attenzione

Non mettere mai segreti sotto il controllo del codice sorgente.

Inizializzare dei segreti utente e archiviare il segreto del client:

cd MyService.Web
dotnet user-secrets init
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "<your-client-secret>"

Quindi, aggiornare appsettings.json per rimuovere il segreto hardcoded.

{
  "AzureAd": {
    "ClientCredentials": [
      {
        "SourceType": "ClientSecret"
      }
    ]
  }
}

Microsoft. Identity.Web supporta più tipi di credenziali. Per la produzione, vedere Panoramica delle credenziali.


Verificare l'implementazione

Usare questo elenco di controllo per confermare il completamento di tutti i passaggi necessari.

Progetto API

  • [ ] Aggiunto pacchetto Microsoft.Identity.Web
  • [ ] Aggiornato appsettings.json con la sezione AzureAd
  • [ ] Aggiornato Program.cs con AddMicrosoftIdentityWebApi
  • [ ] Aggiunta .RequireAuthorization() agli endpoint protetti

Progetto Web/Blazor

  • [ ] Pacchetto Microsoft.Identity.Web aggiunto (v3.3.0+)
  • [ ] Aggiornato appsettings.json con le sezioni AzureAd e WeatherApi
  • [ ] Aggiornato Program.cs con OIDC, acquisizione di token
  • [ ] Aggiunta AddScoped<BlazorAuthenticationChallengeHandler>()
  • [ ] Creato Components/UserInfo.razor (pulsante di login)
  • [ ] Aggiornato MainLayout.razor per includere <UserInfo />
  • [ ] Aggiornato Routes.razor con AuthorizeRouteView
  • [ ] Aggiunto try/catch con ChallengeHandler in ogni pagina che effettua chiamate alle API
  • [ ] Segreto del cliente archiviato nei segreti dell'utente

Verifica

  • [ ] dotnet build ha successo
  • [ ] Registrazioni app create nel centro di amministrazione Microsoft Entra
  • [ ] appsettings.json ha GUID autentici (nessun segnaposto)

Testare e risolvere i problemi

Dopo aver completato l'implementazione, eseguire l'applicazione e verificare il flusso di autenticazione end-to-end.

Eseguire l'applicazione

Avviare Aspire AppHost per avviare entrambi i progetti Web e API.

# From solution root
dotnet restore
dotnet build

# Launch AppHost (starts both Web and API)
dotnet run --project .\MyService.AppHost\MyService.AppHost.csproj

Testare il flusso di autenticazione

  1. Aprire il browser → Blazor Web UI (consultare Aspire dashboard per l'URL).
  2. Selezionare Login → Accedi con Microsoft Entra.
  3. Passare alla pagina Meteo .
  4. Verificare i caricamenti dei dati meteo (dall'API protetta).

Risolvere i problemi comuni

La tabella seguente elenca i problemi frequenti e le relative soluzioni:

Issue Soluzione
401 sulle chiamate API Verificare che gli ambiti in appsettings.json coincidano con l'URI dell'ID app dell'API.
Reindirizzamento OIDC fallito Aggiungere /signin-oidc agli URI di reindirizzamento Microsoft Entra
Token non collegato Assicurarsi che AddMicrosoftIdentityMessageHandler venga chiamato su HttpClient
L'individuazione del servizio non riesce Controllare AppHost.cs i riferimenti sia per i progetti e verificare che siano in esecuzione.
AADSTS65001 Consenso amministratore obbligatorio: concedere il consenso nel Interfaccia di amministrazione di Microsoft Entra
Nessun pulsante di accesso Assicurarsi che UserInfo.razor esista e sia incluso in MainLayout.razor
Ciclo di consenso Assicurarsi che il blocco try/catch con HandleExceptionAsync sia usato in tutte le pagine che richiamano l'API

Abilitare la registrazione MSAL

Quando si risolvano i problemi di autenticazione, abilitare la registrazione MSAL dettagliata per visualizzare i dettagli dell'acquisizione dei token. Aggiungere i livelli di log seguenti a appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.Identity": "Debug",
      "Microsoft.IdentityModel": "Debug"
    }
  }
}

Avvertimento

Disabilitare la registrazione di debug nell'ambiente di produzione perché può essere molto verbosa.

Ispezionare i token

Per eseguire il debug dei problemi del token, decodificare il token JWT in jwt.ms e verificare:

  • aud (audience): corrisponde al Client ID dell'API o all'URI del ID App
  • iss (issuer): corrisponde al tuo tenant (https://login.microsoftonline.com/<tenant-id>/v2.0)
  • scp (ambiti): contiene gli ambiti necessari
  • exp (scadenza): Il token non è scaduto

Esplorare scenari comuni

Le sezioni seguenti illustrano come estendere l'implementazione di base per casi d'uso aggiuntivi.

Proteggere le pagine Blazor

Aggiungere l'attributo [Authorize] alle pagine che richiedono l'autenticazione:

@page "/weather"
@attribute [Authorize]

In alternativa, definire i criteri di autorizzazione in Program.cs:

// Program.cs
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
@attribute [Authorize(Policy = "AdminOnly")]

Convalidare gli ambiti nell'API

Assicurarsi che l'API accetti solo token con ambiti specifici concatenando RequireScope:

app.MapGet("/weatherforecast", () =>
{
    // ... implementation
})
.RequireAuthorization()
.RequireScope("access_as_user");

Usare token solo per app (da servizio a servizio)

Per scenari daemon o chiamate da servizio a servizio senza contesto utente, impostare RequestAppToken su true:

builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
    client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(options =>
{
    options.Scopes.Add("api://<api-client-id>/.default");
    options.RequestAppToken = true;
});

Usa credenziali senza certificato nell'ambiente di produzione

Per le distribuzioni di produzione in Azure, usare l'identità gestita anziché i segreti client. Configurare la ClientCredentials sezione come indicato di seguito:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<tenant-guid>",
    "ClientId":  "<web-app-client-id>",
    "ClientCredentials": [
      {
        "SourceType": "SignedAssertionFromManagedIdentity",
        "ManagedIdentityClientId": "<user-assigned-mi-client-id>"
      }
    ]
  }
}

Per altre informazioni, vedere Autenticazione senza certificati.

Chiamare le API downstream dall'API (per conto di)

Se l'API deve chiamare un'altra API downstream per conto dell'utente, abilita l'acquisizione del token "on-behalf-of" in Program.cs.

// MyService.ApiService/Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

builder.Services.AddDownstreamApi("GraphApi", builder.Configuration.GetSection("GraphApi"));

Aggiungere la configurazione dell'API downstream a appsettings.json:

{
  "GraphApi": {
    "BaseUrl": "https://graph.microsoft.com/v1.0",
    "Scopes": [ "User.Read" ]
  }
}

Chiamare quindi l'API downstream da un endpoint:

{
    var user = await downstreamApi.GetForUserAsync<JsonElement>("GraphApi", "me");
    return user;
}).RequireAuthorization();

Per altre informazioni, vedere Chiamata di API downstream.