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 explica cómo llamar a las API de bajada desde aplicaciones web de ASP.NET Core y OWIN mediante Microsoft. Identity.Web. En las aplicaciones web, se adquieren tokens en nombre del usuario que inició sesión para llamar a las APIs con permisos delegados.
Descripción del flujo de tokens
Cuando un usuario inicia sesión en la aplicación web, puede llamar a las API descendentes (Microsoft Graph, servicios de Azure o API personalizadas) en su nombre. Microsoft. Identity.Web controla la adquisición, el almacenamiento en caché y la actualización automática de tokens.
Revisión del flujo de tokens de usuario
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
Revisión de los requisitos previos
Compruebe que el entorno cumple los siguientes requisitos antes de comenzar.
- Aplicación web configurada con la autenticación de OpenID Connect
- El inicio de sesión del usuario funciona
- Registro de aplicaciones con permisos de API configurados
- Consentimiento del usuario obtenido (o consentimiento del administrador concedido)
Implementación de ASP.NET Core
1. Configuración de la autenticación y la adquisición de tokens
Agregue servicios de autenticación y habilite la adquisición de tokens en el Program.cs archivo.
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. Configurar appsettings.json
Defina la configuración del registro de aplicaciones de Microsoft Entra ID y la API descendente en 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: En el caso de las aplicaciones web que llaman a las API de bajada, necesita credenciales de cliente (certificado o secreto) además de la configuración de inicio de sesión.
3. Agregar compatibilidad para la API descendente
Elija una de las siguientes opciones para registrar las API de bajada.
Opción A: Registrar API con nombre
El siguiente código registra varias API descendentes desde la configuración.
using Microsoft.Identity.Web;
// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
builder.Configuration.GetSection("DownstreamApis"));
Option B: Use Microsoft Graph Helper
El código siguiente registra el cliente del SDK de Microsoft Graph desde la configuración.
// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));
4. Llamar a la API descendente desde el controlador
Inserte IDownstreamApi en el controlador y llame a la API en nombre del usuario que ha iniciado sesión.
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. Llamar a la API descendente desde la Página de Razor
Inserte IDownstreamApi en el modelo de página de Razor y llame a la 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;
}
}
}
Llame a Microsoft Graph
Para las llamadas de Microsoft Graph API, utilice el GraphServiceClient dedicado.
Instalación de paquetes
Instale el paquete de Microsoft Graph para Microsoft. Identity.Web.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Configure el cliente de Graph en el código de inicio.
// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(options =>
{
options.Scopes = "user.read mail.read";
})
.AddInMemoryTokenCaches();
Llamada al Graph API
Inserte GraphServiceClient en el controlador para llamar a los puntos de conexión de 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 });
}
}
Obtenga más información sobre la integración de Microsoft Graph
Llamar a clientes de SDK de Azure
Para llamar a servicios Azure, use MicrosoftIdentityTokenCredential.
Instalación de paquetes
Instale los paquetes de SDK de Azure necesarios.
dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs
Registre la credencial del token de Microsoft Entra en el código de inicio.
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();
Acceso a los servicios de Azure
Inserte la credencial del token y úsela con SDK de Azure clientes.
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);
}
}
Obtenga más información sobre la integración de SDK de Azure
Llamada a API personalizadas con IDownstreamApi
Para sus propias API REST, IDownstreamApi proporciona un enfoque sencillo y basado en la configuración.
Configuración de la API
Configure la configuración de la API descendente en appsettings.json.
{
"DownstreamApis": {
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"],
"RequestAppToken": false
}
}
}
Envío de solicitudes GET
Recupere datos de la API descendente con parámetros de consulta opcionales.
// 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"
};
});
Envío de solicitudes POST
Cree un nuevo recurso en la API de bajada publicando un cuerpo de solicitud.
var newItem = new CreateItemRequest
{
Name = "New Item",
Description = "Item description"
};
var created = await _downstreamApi.PostForUserAsync<CreateItemRequest, CreatedItem>(
"MyAPI",
newItem,
options => options.RelativePath = "api/items");
Envío de solicitudes PUT y DELETE
Actualice o elimine recursos en la API de bajada.
// 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");
Más información sobre las llamadas API personalizadas
Usar IAuthorizationHeaderProvider (avanzado)
Para un control máximo sobre las solicitudes HTTP, use IAuthorizationHeaderProvider.
Registro del cliente HTTP
Registre un cliente HTTP con nombre para la API de nivel inferior.
builder.Services.AddHttpClient("MyAPI", client =>
{
client.BaseAddress = new Uri("https://myapi.example.com");
});
Creación de solicitudes HTTP personalizadas
Compile y envíe solicitudes HTTP con encabezados personalizados y autorización.
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>();
}
}
Más información sobre la lógica HTTP personalizada
Controlar el consentimiento incremental y el acceso condicional
Al llamar a las API de bajada, es posible que la aplicación tenga que controlar escenarios en los que se requiera la interacción del usuario. Esto sucede en tres escenarios principales:
- Consentimiento incremental : solicitar permisos adicionales más allá de lo que se concedió inicialmente
- Acceso condicional : cumplir los requisitos de seguridad como MFA, cumplimiento de dispositivos o directivas de ubicación
- Expulsión de la caché de tokens: repoblar la caché de tokens después de reiniciar la aplicación o de que expire la caché
Microsoft. Identity.Web proporciona control automático de estos escenarios con un código mínimo necesario.
Descripción del flujo
Cuándo Microsoft. Identity.Web detecta que se necesita interacción del usuario, genera una MicrosoftIdentityWebChallengeUserException. El marco lo controla automáticamente a través del [AuthorizeForScopes] atributo o el MicrosoftIdentityConsentAndConditionalAccessHandler servicio (para Blazor), que:
- Redirige al usuario a Microsoft Entra ID para la autenticación o consentimiento
- Conserva la dirección URL de solicitud original.
- Devuelve el usuario a su destino previsto después de completar el flujo.
- Almacena en caché los tokens recién adquiridos.
Revisión de los requisitos previos
Para habilitar el control automático del consentimiento, asegúrese de que Program.cs incluye la siguiente configuración.
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
Aplicar [AuthorizeForScopes] en controladores MVC
El atributo [AuthorizeForScopes], establecido en controladores o acciones de controlador, gestiona MicrosoftIdentityWebChallengeUserException automáticamente desafiando al usuario cuando se necesitan permisos adicionales.
Declarar ámbitos insertados
Especifique los ámbitos necesarios directamente en el atributo .
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);
}
}
Configuración de ámbitos desde appsettings
Almacene alcances en appsettings.json para mejorar la capacidad de mantenimiento.
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" ]
}
}
}
Controlador:
[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");
}
}
Configuración de Id. externa de Microsoft Entra con flujos de usuario
Para las aplicaciones con ID Externo (B2C) que tienen varios flujos de usuario, especifique el flujo de usuario en el atributo.
[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");
}
}
Aplicar [AuthorizeForScopes] en Razor Pages
Aplique [AuthorizeForScopes] a la clase de modelo de página:
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");
}
}
Controlar el consentimiento en Blazor Server
Las aplicaciones blazor Server requieren un control explícito de excepciones mediante el MicrosoftIdentityConsentAndConditionalAccessHandler servicio.
Configuración de Program.cs
Registre el controlador de consentimiento para Blazor Server en el código de inicio.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApis("TodoList", builder.Configuration.GetSection("DownstreamApis"))
.AddInMemoryTokenCaches();
// Register the consent handler for Blazor
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
Creación del componente Blazor
Envuelva las llamadas a la API en bloques try-catch y use ConsentHandler.HandleException() para gestionar los desafíos de consentimiento.
@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);
}
}
}
Controlar excepciones manualmente (avanzado)
Si necesita lógica de flujo de consentimiento personalizada, controle MicrosoftIdentityWebChallengeUserException explícitamente:
[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");
}
}
}
Control de escenarios de acceso condicional
Las directivas de acceso condicional pueden requerir factores de autenticación adicionales. La gestión es idéntica al consentimiento incremental.
[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);
}
}
Desencadenadores de acceso condicional comunes:
- Autenticación multifactor (MFA)
- Requisito de dispositivo compatible
- Ubicación de red de confianza
- Aceptación de los términos de uso
- Requisito de cambio de contraseña
Seguimiento de los procedimientos recomendados
Aplique estas recomendaciones al implementar el consentimiento y el control del acceso condicional.
Uso [AuthorizeForScopes] - Enfoque más sencillo para controladores MVC y Razor Pages
Almacenar ámbitos en la configuración - Utilice ScopeKeySection = "DownstreamApis:ApiName:Scopes:0" para hacer referencia a los ámbitos en appsettings.json
Aplicar en el nivel de controlador : establecimiento de ámbitos predeterminados en el controlador, invalidación en acciones específicas
Control de excepciones en Blazor - siempre encapsule las llamadas a la API con try-catch y use ConsentHandler.HandleException()
Permitir que se vuelvan a producir excepciones : si detecta MicrosoftIdentityWebChallengeUserException, vuelva a iniciarla para [AuthorizeForScopes] que pueda procesarla.
Prueba del acceso condicional : compruebe que la aplicación controla MFA y otras directivas de CA correctamente.
No suprimir excepciones - Capturar sin relanzar interrumpe el flujo de consentimiento.
No almacenar en caché las respuestas indefinidamente : los tokens expiran; diseño para volver a autenticar
Comparación de permisos estáticos y consentimiento incremental
Permisos estáticos (consentimiento del administrador)
Todos los permisos se solicitan durante el registro de aplicaciones y los da su consentimiento un administrador de inquilinos:
Ventajas:
- Los usuarios nunca ven las solicitudes de consentimiento
- Necesario para aplicaciones de Microsoft de primera parte
- Experiencia de usuario más sencilla
Desventajas:
- Requiere la participación del administrador de inquilinos
- Con privilegios excesivos desde el principio
- Menos flexible para escenarios multiinquilino
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
});
Consentimiento incremental (dinámico)
Los permisos se solicitan según sea necesario durante el tiempo de ejecución:
Ventajas:
- Mejor seguridad (principio de privilegios mínimos)
- Los usuarios dan su consentimiento a lo que realmente usan
- Funciona para aplicaciones multiusuario
Desventajas:
- Es posible que los usuarios experimenten interrupciones debido a solicitudes de consentimiento
- Requiere manejo
MicrosoftIdentityWebChallengeUserException
Recomendación: Uso del consentimiento incremental para aplicaciones multiinquilino; usar permisos estáticos para aplicaciones empresariales de primera entidad en las que se garantiza el consentimiento del administrador
Configuración del almacenamiento en caché de tokens
Microsoft. Identity.Web almacena en caché tokens para mejorar el rendimiento y reducir las llamadas a Microsoft Entra.
Uso de la caché en memoria (valor predeterminado)
Agregue una caché de tokens en memoria para escenarios de desarrollo o de servidor único.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(); // In-memory cache
Usar para:
- Desarrollo
- Implementaciones de servidor único
- Base de usuarios pequeños
Limitaciones:
- No se comparte entre instancias
- Perdido en el reinicio de la aplicación
- El consumo de memoria crece con los usuarios
Uso de la caché distribuida (recomendada para producción)
Configure una caché distribuida como Redis o SQL Server para implementaciones de producción.
// 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();
Usar para:
- Implementaciones de varios servidores (con equilibrio de carga)
- Escenarios de alta disponibilidad
- Base de usuarios de gran tamaño
- Caché persistente entre reinicios
Manejo de fallos en la adquisición de tokens
Detectar excepciones comunes
En el código siguiente se muestra cómo detectar y controlar las excepciones de adquisición de tokens más comunes.
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");
}
Implementar una degradación gradual
Cargue datos opcionales de las API de bajada y vuelva a los valores predeterminados cuando se produzca un error en las llamadas.
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);
}
Implementación de OWIN (.NET Framework)
Para las aplicaciones web basadas en OWIN en .NET Framework, siga estos pasos.
1. Instalar paquetes
Instale los paquetes NuGet necesarios.
Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb
2. Configurar inicio
Configura la autenticación y adquisición de tokens de Microsoft Entra en la clase de inicio de 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. Llamada a la API de bajada
Adquiera un token y llame a la API de bajada desde un controlador 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: la compatibilidad con OWIN tiene algunas diferencias de ASP.NET Core. Consulte la documentación de OWIN para obtener más información.
Seguir los procedimientos recomendados de seguridad
Administrar alcances
Aplique el principio de privilegios mínimos al solicitar permisos de API.
Sí:
- Solicitar solo los ámbitos que necesita
- Uso del consentimiento incremental para características avanzadas
- Documentar los ámbitos necesarios en tu aplicación
No:
- Solicitar ámbitos innecesarios por adelantado
- Solicitar ámbitos solo de administrador sin justificación
- Supongamos que se concederán todos los ámbitos
Maneja tokens de forma segura
Siga estas instrucciones para proteger los tokens de acceso en la aplicación.
Sí:
- Deje que Microsoft.Identity.Web gestione los tokens
- Uso de la caché distribuida en producción
- Gestionar adecuadamente los fallos en la adquisición de tokens
No:
- Almacene los tokens usted mismo
- Tokens de acceso de registro
- Envío de tokens al código del lado cliente
Manejo de errores
Implemente un control sólido de errores para errores de autenticación y llamadas API.
Sí:
- Detectar y controlar excepciones de consentimiento
- Proporcionar mensajes de error claros a los usuarios
- Registro de errores para la depuración
No:
- Exponer errores de token a los usuarios
- Error silencioso de llamadas API
- Omitir excepciones de autenticación
Solucionar problemas comunes
Revise estas soluciones para ver los errores de autenticación detectados con frecuencia.
Problema: "AADSTS65001: el usuario o administrador no ha consentido"
Causa: El usuario no ha consentido los permisos necesarios.
Solution:
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Redirect to consent page
return Challenge(
new AuthenticationProperties { RedirectUri = Request.Path },
OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: "AADSTS50076: autenticación multifactor requerida"
Causa: El usuario debe completar MFA.
Solution:
catch (MsalUiRequiredException)
{
// Redirect user to sign in with MFA
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
Problema: Los tokens no persisten en los reinicios de la aplicación
Causa: Uso de caché en memoria.
Solution: Cambiar a caché distribuida (Redis, SQL Server o Cosmos DB).
Problema: 401 No autorizado desde la API de nivel inferior
Causas posibles:
- Ámbitos incorrectos solicitados
- Permiso de API no concedido en el registro de aplicaciones
- Token expirado
Solution:
- Verificar que los ámbitos en appsettings.json coincidan con los requisitos de la API
- Comprobación de que el registro de aplicaciones tiene permisos de API
- Asegúrese de que los tokens se almacenan en caché y se actualizan
Para obtener diagnósticos detallados: Consulte Guía de registro y diagnóstico para ver los identificadores de correlación, la depuración de caché de tokens y patrones de solución de problemas completos.
Optimización del rendimiento
Planeamiento de la estrategia de almacenamiento en caché de tokens
Seleccione una estrategia de almacenamiento en caché que coincida con la topología de implementación.
- Uso de la caché distribuida para implementaciones de varios servidores
- Configuración de la expiración de caché adecuada
- Supervisión del rendimiento de la memoria caché
Minimizar las solicitudes de token
Microsoft. Identity.Web almacena en caché los tokens automáticamente. Ambas llamadas en el ejemplo siguiente reutilizan el mismo token almacenado en caché.
// 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");
Realización de llamadas API paralelas
Llame simultáneamente a varias API de bajada para reducir la latencia general.
// 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;
Exploración del contenido relacionado
Busque instrucciones adicionales para escenarios relacionados.