Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En esta guía se muestra cómo proteger una aplicación distribuida .NET Aspire con Microsoft Entra ID autenticación y autorización. Abarca:
-
Front-end de Blazor Server (
MyService.Web): inicio de sesión de usuario con OpenID Connect y adquisición de tokens -
Backend de API protegida (
MyService.ApiService): validación de JWT mediante Microsoft.Identity.Web - Flujo integral: Blazor adquiere tokens de acceso y llama a la API protegida con el descubrimiento de servicios Aspire
En esta guía se supone que ha iniciado con un proyecto Aspire creado mediante el siguiente comando:
aspire new aspire-starter --name MyService
Prerrequisitos
- SDK de .NET 9 o versiones posteriores
- .NET Aspire CLI - Consulte Instalar Aspire CLI
Microsoft Entra tenant — ConsulteRegistrar aplicaciones en Microsoft Entra ID para la configuración
Sugerencia
¿No está familiarizado con Aspire? Consulte visión general de .NET Aspire.
Descripción del flujo de trabajo en dos fases
En esta guía se sigue un enfoque de dos fases:
| Fase | ¿Qué ocurre? | Resultado |
|---|---|---|
| Fase 1 | Agregar código de autenticación con valores de marcador de posición | Compilación de aplicación, pero no se ejecuta |
| Fase 2 | Configurar registros de aplicaciones de Microsoft Entra | La aplicación se ejecuta con autenticación real |
Registro de aplicaciones en Microsoft Entra ID
Para que la aplicación pueda autenticar a los usuarios, necesita dos registros de aplicaciones en Microsoft Entra:
| Registro de aplicaciones | propósito | Configuración de claves |
|---|---|---|
API (MyService.ApiService) |
Valida los tokens entrantes. | URI de id. de aplicación, access_as_user ámbito |
Aplicación web (MyService.Web) |
Inicia sesión de usuarios y adquiere tokens | URI de redirección, secreto de cliente, permisos de API |
Si ya tiene registros de aplicaciones configurados, necesita estos valores para appsettings.json:
- TenantId: identificador de inquilino de Microsoft Entra
- ClientId de API — Identificación de la aplicación (cliente) del registro de la aplicación de API
-
URI de id. de aplicación de API : normalmente
api://<api-client-id>(se usa enAudiencesyScopes) - ID de la aplicación web — ID de la aplicación (cliente) de la aplicación web
- Secreto de cliente (o certificado): credencial para la aplicación web (almacén en secretos de usuario, no appsettings.json)
-
Ámbitos : los ámbitos que solicita la aplicación web, por ejemplo,
api://<api-client-id>/.defaultoapi://<api-client-id>/access_as_user
Paso 1: Registro de la API
- Vaya al Centro de administración de Microsoft Entra>Identity (Identidad)>Applications (Aplicaciones)>Registros de aplicaciones (Registros de aplicaciones).
- Seleccione Nuevo registro.
-
Nombre:
MyService.ApiService - Tipos de cuenta admitidos: Solo las cuentas de este directorio organizativo (inquilino único)
- Seleccione Registrar.
-
Nombre:
- Vaya a Exponer una API>Agregar junto a URI de identificador de aplicación.
- Acepte el valor predeterminado (
api://<client-id>) o personalícelo. - Seleccione Agregar un ámbito:
-
Nombre del ámbito:
access_as_user - Quién puede dar su consentimiento: Administradores y usuarios
- Nombre para mostrar del consentimiento del administrador: Acceso a MyService API
- Descripción del consentimiento del administrador: Permite que la aplicación acceda a MyService API en nombre del usuario que ha iniciado sesión.
- Selecciona la opción Agregar un ámbito.
-
Nombre del ámbito:
- Acepte el valor predeterminado (
- Copie el identificador de aplicación (cliente): necesitará esto para ambos
appsettings.jsonarchivos.
Para más información, consulte Inicio rápido: Configuración de una aplicación para exponer una API web.
Paso 2: Registrar la aplicación web
- Vaya a Registros de aplicaciones>Nuevo registro.
-
Nombre:
MyService.Web - Tipos de cuenta admitidos: Solo las cuentas de este directorio organizativo
-
URI de redirección: Seleccione Web y escriba la dirección URL de la aplicación +
/signin-oidc- Para el desarrollo local:
https://localhost:7001/signin-oidc(verifiquelaunchSettings.jsonpara el puerto real)
- Para el desarrollo local:
- Seleccione Registrar.
-
Nombre:
- Vaya a Autenticación>Agregar URI para agregar todas las direcciones URL de desarrollo (desde
launchSettings.json). - Vaya a Certificados y secretos>Secretos de cliente>Nuevo secreto de cliente.
- Agregue una descripción y una expiración.
- Copie el valor del secreto inmediatamente; no se volverá a mostrar.
- Vaya a Permisos> de APIAgregar un permiso>Mis API.
- Seleccione
MyService.ApiService. - Seleccione
access_as_user>Agregar permisos. - Seleccione Conceder consentimiento del administrador para [inquilino] (o se solicita a los usuarios al primer uso).
- Seleccione
- Copie el identificador de aplicación (cliente) para la aplicación web.
appsettings.json
Nota:
Algunas organizaciones no permiten secretos de cliente. Para obtener alternativas, consulte Credenciales de certificado o autenticación sin certificado.
Para obtener más información, consulte Inicio rápido: Registro de una aplicación.
Paso 3: Actualización de la configuración
Después de crear los registros de aplicaciones, actualice los appsettings.json archivos:
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"]
}
}
Aplicación 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"]
}
}
Almacene el secreto de forma segura:
cd MyService.Web
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "YOUR_SECRET_VALUE"
| Importancia | Dónde encontrar |
|---|---|
TenantId |
Centro de administración Microsoft Entra > Información general > Id. de inquilino |
API ClientId |
Registros de aplicaciones > MyService.ApiService > ID de aplicación (cliente) |
Web ClientId |
Registros de aplicaciones > MyService.Web > ID de aplicación (cliente) |
Client Secret |
Creado en el paso 2 (copiar inmediatamente cuando se creó) |
Nota:
La plantilla aspire starter crea automáticamente una WeatherApiClient clase en el MyService.Web proyecto. Este HttpClient tipado se utiliza a lo largo de esta guía para demostrar cómo llamar a la API protegida. No es necesario crear esta clase usted mismo, sino que forma parte de la plantilla.
Empiece rápidamente
En esta sección se proporciona una referencia condensada para agregar autenticación. Para ver tutoriales detallados, vea Parte 1 y Parte 2.
API (MyService.ApiService)
Instale el paquete NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Agregue la configuración de Microsoft Entra a appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-id>",
"ClientId": "<api-client-id>",
"Audiences": ["api://<api-client-id>"]
}
}
Registre la autenticación y la autorización en Program.cs:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization();
// ...
app.UseAuthentication();
app.UseAuthorization();
// ...
app.MapGet("/weatherforecast", () => { /* ... */ }).RequireAuthorization();
Aplicación web (MyService.Web)
Instale el paquete NuGet Microsoft.Identity.Web:
dotnet add package Microsoft.Identity.Web
Agregue la configuración de 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"] }
}
Configure la autenticación, la adquisición de tokens y el cliente de API de bajada en 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();
MicrosoftIdentityMessageHandler adquiere y adjunta tokens automáticamente, mientras que BlazorAuthenticationChallengeHandler maneja los desafíos de consentimiento y acceso condicional.
Importante
No olvide crear UserInfo.razor para el botón de inicio de sesión. Consulte Adición de componentes de la interfaz de usuario de Blazor para obtener más información.
Nota:
BlazorAuthenticationChallengeHandler y LoginLogoutEndpointRouteBuilderExtensions se incluyen en Microsoft.Identity.Web (v3.3.0+). No se requiere copia de archivos.
Identificación de archivos que se van a modificar
En la tabla siguiente se enumeran los archivos que cambia en cada proyecto:
| Proyecto | Archivo | Changes |
|---|---|---|
| ApiService | Program.cs |
Autenticación JWT Bearer, intermediario de autorización |
appsettings.json |
Configuración de Microsoft Entra | |
.csproj |
Agregar Microsoft.Identity.Web |
|
| Web | Program.cs |
Autenticación de OIDC, adquisición de tokens, BlazorAuthenticationChallengeHandler |
appsettings.json |
configuración de Microsoft Entra, ámbitos de API de bajada | |
.csproj |
Agregar Microsoft.Identity.Web (v3.3.0+) |
|
Components/UserInfo.razor |
Interfaz de usuario del botón de inicio de sesión (nuevo archivo) | |
Components/Layout/MainLayout.razor |
Incluir componente UserInfo | |
Components/Routes.razor |
AuthorizeRouteView para páginas protegidas | |
| Las páginas que llaman a APIs | Prueba y captura con ChallengeHandler |
Descripción del flujo de autenticación
En el diagrama siguiente se muestra cómo interactúa el front-end de Blazor, Microsoft Entra y la API protegida:
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
- El usuario visita la aplicación Blazor → No autenticada → ve el botón "Iniciar sesión".
-
El usuario selecciona Iniciar sesión → Redirige a
/authentication/login→ Desafío OIDC → Microsoft Entra. -
El usuario inicia sesión → Microsoft Entra redirige a
/signin-oidc→ cookie establecida. -
El usuario navega a la página Weather → Blazor llama a
WeatherApiClient.GetAsync(). -
MicrosoftIdentityMessageHandlerintercepta la solicitud, adquiere un token de la memoria caché (o actualiza de forma silenciosa) y adjunta elAuthorization: Bearer <token>encabezado. - API recibe la solicitud → Microsoft. Identity.Web valida el → JWT devuelve datos.
- Blazor representa los datos meteorológicos.
Revisión de la estructura de la solución
La plantilla aspire starter crea el siguiente diseño de proyecto:
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: Protección del back-end de api con Microsoft. Identity.Web
En esta sección se configura el proyecto de API para validar los tokens de portador JWT emitidos por Microsoft Entra.
Agregue el paquete Microsoft.Identity.Web
Ejecute el siguiente comando para instalar el Microsoft. Paquete NuGet Identity.Web:
cd MyService.ApiService
dotnet add package Microsoft.Identity.Web
Configuración de Microsoft Entra
Agregue la configuración de 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": "*"
}
Propiedades clave:
-
ClientId: ID de registro de aplicación de API de Microsoft Entra -
TenantId: identificador de tenant de Microsoft Entra o"organizations"para multi-tenant o"common"para cualquier cuenta de Microsoft -
Audiences: Destinatarios de tokens válidos (normalmente el URI de ID de aplicación)
Actualizar el archivo Program.cs de la API
Reemplace el contenido de MyService.ApiService/Program.cs por el código siguiente para agregar la autenticación de portador JWT y proteger los puntos de conexión:
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);
}
Cambios clave:
- Registrar la autenticación de tipo Bearer JWT con
AddMicrosoftIdentityWebApi - Agregar
app.UseAuthentication()yapp.UseAuthorization()middleware - Aplicar
.RequireAuthorization()a puntos de conexión protegidos
Prueba de la API protegida
Compruebe que la API rechaza solicitudes no autenticadas y acepta tokens válidos.
Envíe una solicitud sin un token:
curl https://localhost:<PORT>/weatherforecast
# Expected: 401 Unauthorized
Envíe una solicitud con un token válido:
curl -H "Authorization: Bearer <TOKEN>" https://localhost:<PORT>/weatherforecast
# Expected: 200 OK with weather data
Parte 2: Configuración del front-end de Blazor para la autenticación
La aplicación Blazor Server usa Microsoft. Identity.Web a:
- Autenticar usuarios con OIDC
- Adquisición de tokens de acceso para llamar a la API
- Adjunte tokens a las solicitudes HTTP salientes
Agregue el paquete Microsoft.Identity.Web
Ejecute el siguiente comando para instalar el Microsoft. Paquete NuGet Identity.Web:
cd MyService.Web
dotnet add package Microsoft.Identity.Web
Configurar ajustes de Microsoft Entra
Agregue la configuración de Microsoft Entra y los ámbitos de API descendente 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": "*"
}
Detalles de configuración:
-
ClientId: id. de registro de aplicación web (no el identificador de API) -
ClientCredentials: credenciales de la aplicación web para adquirir tokens. Admite varios tipos de credenciales. Consulte Resumen de credenciales para las opciones listas para producción. -
Scopes: debe coincidir con el URI del identificador de aplicación de la API con el sufijo/.default.
Advertencia
Para producción, use certificados o identidad administrada en lugar de secretos de cliente. Consulte Autenticación sin certificados para obtener el enfoque recomendado.
Actualizar Program.cs de la aplicación web
Reemplace el contenido de MyService.Web/Program.cs por el código siguiente para configurar la autenticación de OIDC, la adquisición de tokens y el cliente de API de bajada:
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();
Puntos clave:
-
AddMicrosoftIdentityWebApp: configura la autenticación de OIDC. -
EnableTokenAcquisitionToCallDownstreamApi: habilita la adquisición de tokens para las API de bajada. -
AddScoped<BlazorAuthenticationChallengeHandler>: controla el consentimiento incremental y el acceso condicional en Blazor Server. -
AddMicrosoftIdentityMessageHandler: Adjunta automáticamente tokens de portador a las solicitudes de HttpClient. -
https+http://apiservice: La detección de servicios Aspire resuelve esto a la URL real de la API. -
Orden de middleware:
UseAuthentication()→UseAuthorization()→ puntos de conexión
La AddMicrosoftIdentityMessageHandler extensión admite varios patrones de configuración:
Opción 1: Configuración de appsettings.json (mostrada anteriormente)
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));
Opción 2: Configuración en línea con delegado de Action
.AddMicrosoftIdentityMessageHandler(options =>
{
options.Scopes.Add("api://<api-client-id>/.default");
});
Opción 3: Configuración por solicitud (sin parámetros)
.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);
Adición de componentes de la interfaz de usuario de Blazor
Importante
Este paso se olvida con frecuencia. Sin el componente UserInfo, los usuarios no tienen forma de iniciar sesión.
BlazorAuthenticationChallengeHandler y LoginLogoutEndpointRouteBuilderExtensions se envían con Microsoft.Identity.Web v3.3.0+. Están disponibles automáticamente una vez que hace referencia al paquete; no se requiere copia de archivos.
Crear 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>
Agregar al diseño: Incluya <UserInfo /> en 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>
Actualizar Routes.razor para AuthorizeRouteView
Reemplace RouteView con AuthorizeRouteView en 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>
Control de excepciones en páginas que llaman a las API
Blazor Server requiere un control explícito de excepciones para el acceso condicional y el consentimiento. Debes controlar MicrosoftIdentityWebChallengeUserException en todas las páginas que llamen a una API descendente, a menos que la aplicación esté preautorizada y solicites todos los permisos con antelación en Program.cs.
En el ejemplo siguiente Weather.razor se muestra el control de excepciones adecuado:
@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}";
}
}
}
}
El patrón funciona de la siguiente manera:
-
IsAuthenticatedAsync()comprueba si el usuario ha iniciado sesión antes de realizar llamadas API. -
HandleExceptionAsync()capturaMicrosoftIdentityWebChallengeUserException(o como InnerException). - Si se trata de una excepción de autenticación, se redirige al usuario para volver a autenticarse con los atributos o ámbitos necesarios.
- Si no es una excepción de desafío,
HandleExceptionAsyncdevuelvefalsepara que pueda controlar el error usted mismo.
Almacenar el secreto del cliente en los secretos de usuario
Use el administrador de secretos de .NET para almacenar el secreto de cliente de forma segura durante el desarrollo.
Precaución
Nunca confirme secretos en el control de código fuente.
Inicialice los secretos de usuario y almacene el secreto de cliente:
cd MyService.Web
dotnet user-secrets init
dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "<your-client-secret>"
A continuación, actualice appsettings.json para quitar el secreto codificado de forma codificada:
{
"AzureAd": {
"ClientCredentials": [
{
"SourceType": "ClientSecret"
}
]
}
}
Microsoft. Identity.Web admite varios tipos de credenciales. Para la producción, consulte Descripción general de las credenciales.
Comprobación de la implementación
Use esta lista de comprobación para confirmar que completó todos los pasos necesarios.
Proyecto de API
- [ ] Se ha agregado
Microsoft.Identity.Webpaquete - [ ] Actualizado
appsettings.jsonla sección conAzureAd - [ ] Actualizado
Program.csconAddMicrosoftIdentityWebApi - [ ] Se ha agregado
.RequireAuthorization()a los puntos de conexión protegidos.
Proyecto web/Blazor
- [ ] Se ha agregado
Microsoft.Identity.Webpaquete (v3.3.0+) - [ ] Actualizado
appsettings.jsoncon las seccionesAzureAdyWeatherApi - [ ] Actualizado
Program.cscon OIDC y la adquisición de tokens - [ ] Elemento agregado
AddScoped<BlazorAuthenticationChallengeHandler>() - [ ] Creó
Components/UserInfo.razor(el botón de inicio de sesión) - [ ] Actualizado
MainLayout.razorpara incluir<UserInfo /> - [ ] Actualizado
Routes.razorconAuthorizeRouteView - [ ] Se ha agregado try/catch con
ChallengeHandleren cada página que llama a las API. - [ ] Secreto de cliente almacenado en secretos de usuario
Comprobación
- [ ]
dotnet buildtiene éxito - Registros de aplicaciones creadas en el centro de administración de Microsoft Entra
- [ ]
appsettings.jsontiene GUID reales (sin marcadores de posición)
Prueba y solución de problemas
Después de completar la implementación, ejecute la aplicación y compruebe el flujo de autenticación de un extremo a otro.
Ejecutar la aplicación
Inicie Aspire AppHost para iniciar los proyectos web y de API:
# From solution root
dotnet restore
dotnet build
# Launch AppHost (starts both Web and API)
dotnet run --project .\MyService.AppHost\MyService.AppHost.csproj
Prueba del flujo de autenticación
- Abra el explorador → interfaz de usuario web de Blazor (compruebe el panel Aspire para ver la dirección URL).
- Seleccione Login → Iniciar sesión con Microsoft Entra.
- Vaya a la página Tiempo .
- Compruebe las cargas de datos meteorológicos (desde la API protegida).
Resolución de problemas comunes
En la tabla siguiente se enumeran los problemas frecuentes y sus soluciones:
| Cuestión | Solución |
|---|---|
| Error 401 en llamadas API | Verifica que los ámbitos en appsettings.json coincidan con el URI de ID de la aplicación de la API |
| Error de redirección de OIDC | Agregar /signin-oidc a los URI de redirección de Microsoft Entra |
| Token no asignado | Asegúrese de que se llame a AddMicrosoftIdentityMessageHandler en el HttpClient |
| Error en la detección de servicios | Compruebe que AppHost.cs hace referencia a ambos proyectos y que se están ejecutando. |
| AADSTS65001 | Consentimiento del administrador necesario: conceda consentimiento en el Centro de administración Microsoft Entra |
| No hay ningún botón de inicio de sesión | Asegúrese de que UserInfo.razor existe y está incluido en MainLayout.razor |
| Bucle de consentimiento | Asegúrese de que try/catch with HandleExceptionAsync está en todas las páginas de llamadas api |
Habilita el registro de MSAL
Al solucionar problemas de autenticación, habilite el registro detallado de MSAL para ver los detalles de la adquisición de tokens. Agregue los siguientes niveles de registro a appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Identity": "Debug",
"Microsoft.IdentityModel": "Debug"
}
}
}
Advertencia
Deshabilite el registro de depuración en producción porque puede ser muy verboso.
Inspección de tokens
Para depurar problemas de token, descodifique el JWT en jwt.ms y compruebe lo siguiente:
-
aud(audiencia): coincide con el identificador de cliente de la API o el URI del identificador de aplicación. -
iss(emisor): coincide con el inquilino (https://login.microsoftonline.com/<tenant-id>/v2.0) -
scp(ámbitos): contiene los ámbitos necesarios. -
exp(expiración): el token no ha expirado
Exploración de escenarios comunes
En las secciones siguientes se muestra cómo ampliar la implementación base para casos de uso adicionales.
Protección de páginas de Blazor
Agregue el [Authorize] atributo a las páginas que requieren autenticación:
@page "/weather"
@attribute [Authorize]
O bien, defina directivas de autorización en Program.cs:
// Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
@attribute [Authorize(Policy = "AdminOnly")]
Validación de ámbitos en la API
Asegúrese de que la API solo acepta tokens con ámbitos específicos mediante el encadenamiento RequireScope:
app.MapGet("/weatherforecast", () =>
{
// ... implementation
})
.RequireAuthorization()
.RequireScope("access_as_user");
Uso de tokens exclusivamente de aplicación (servicio a servicio)
Para escenarios de demonio o llamadas de servicio a servicio sin un contexto de usuario, establezca RequestAppToken como 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;
});
Uso de credenciales sin certificado para producción
En el caso de las implementaciones de producción en Azure, use la identidad administrada en lugar de los secretos de cliente. Configure la sección ClientCredentials de la siguiente manera:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<tenant-guid>",
"ClientId": "<web-app-client-id>",
"ClientCredentials": [
{
"SourceType": "SignedAssertionFromManagedIdentity",
"ManagedIdentityClientId": "<user-assigned-mi-client-id>"
}
]
}
}
Para obtener más información, consulte Autenticación sin certificados.
Llamar a las API descendentes desde la API (en nombre de)
Si su API necesita llamar a otra API secundaria en nombre del usuario, habilite la adquisición de tokens en nombre de: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"));
Agregue la configuración de la API de destino a appsettings.json:
{
"GraphApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "User.Read" ]
}
}
A continuación, llame a la API descendente desde un punto de conexión.
{
var user = await downstreamApi.GetForUserAsync<JsonElement>("GraphApi", "me");
return user;
}).RequireAuthorization();
Para más información, consulte Llamada a las API de bajada.
Contenido relacionado
- Inicio rápido: Inicio de sesión de usuarios en una aplicación web
- Inicio rápido: Protección de una API web
- Llamada a API personalizadas
- Introducción a las credenciales
- Plataforma de identidad de Microsoft
- Información general sobre .NET Aspire
- Compilación de la primera aplicación Aspire
- determinación de servicios en .NET Aspire