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.
Microsoft. Identity.Web proporciona valores predeterminados seguros para la autenticación y autorización en ASP.NET Core aplicaciones que se integran con Microsoft Entra ID. Puede personalizar muchos aspectos del comportamiento de autenticación al tiempo que conserva las características de seguridad integradas de la biblioteca.
Identificación de áreas personalizables
| Area | Opciones de personalización |
|---|---|
| Configuración | Todas las MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions propiedades |
| Eventos | Eventos OpenID Connect (OnTokenValidated, OnRedirectToIdentityProvider, etc.) |
| Adquisición de tokens | Identificadores de correlación, parámetros de consulta adicionales |
| Notificaciones | Adición de declaraciones personalizadas a ClaimsPrincipal |
| interfaz de usuario | Páginas de cierre de sesión, comportamiento de redirección |
| Inicio de sesión | Sugerencias de inicio de sesión, sugerencias de dominio |
Elección de un método de personalización
En la tabla siguiente se resumen las áreas que puede personalizar y qué admite cada área.
Use uno de estos dos enfoques para personalizar las opciones:
-
Configure<TOptions>- Configura las opciones antes de que se usen. -
PostConfigure<TOptions>- Configura las opciones después de todas lasConfigurellamadas.
Orden de ejecución:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Configuración de las opciones de autenticación
En esta sección se muestra cómo configurar las distintas clases de opción de autenticación que Microsoft. Identity.Web usa.
Entender la asignación de configuración
La "AzureAd" sección en appsettings.json se asigna a varias clases.
Puede usar cualquier propiedad de estas clases en la configuración.
Patrón 1: Configurar MicrosoftIdentityOptions
El siguiente código personaliza MicrosoftIdentityOptions para habilitar el registro de PII, establecer capacidades del cliente y ajustar los parámetros de validación de tokens.
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
// Customize Microsoft Identity options
builder.Services.Configure<MicrosoftIdentityOptions>(options =>
{
// Enable PII logging (development only!)
options.EnablePiiLogging = true;
// Custom client capabilities
options.ClientCapabilities = new[] { "CP1", "CP2" };
// Override token validation parameters
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
});
var app = builder.Build();
Patrón 2: Configurar OpenIdConnectOptions (aplicaciones web)
El código siguiente personaliza OpenIdConnectOptions para que una aplicación web establezca el tipo de respuesta, agregue ámbitos y configure las opciones de validación de cookies y tokens:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));
// Customize OpenIdConnect options
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
// Override response type
options.ResponseType = "code id_token";
// Add extra scopes
options.Scope.Add("offline_access");
options.Scope.Add("profile");
// Customize token validation
options.TokenValidationParameters.NameClaimType = "preferred_username";
options.TokenValidationParameters.RoleClaimType = "roles";
// Set redirect URI
options.CallbackPath = "/signin-oidc";
// Configure cookie options
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
Patrón 3: Configuración de JwtBearerOptions (API web)
El código siguiente personaliza JwtBearerOptions para que una API web establezca audiencias válidas, mapeo de reclamaciones y validación del tiempo de vida del token.
using Microsoft.AspNetCore.Authentication.JwtBearer;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
// Customize JWT Bearer options
builder.Services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme,
options =>
{
// Customize audience validation
options.TokenValidationParameters.ValidAudiences = new[]
{
"api://your-api-client-id",
"https://your-api.com"
};
// Set custom claim mappings
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "roles";
// Customize token validation
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.Zero; // No tolerance
});
Patrón 4: Configurar opciones de cookies
El código siguiente configura la directiva de cookies y las opciones de autenticación de cookies para la aplicación, incluida la configuración de seguridad y el comportamiento de expiración:
using Microsoft.AspNetCore.Authentication.Cookies;
// Configure cookie policy
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Lax;
options.Secure = CookieSecurePolicy.Always;
options.HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always;
});
// Configure cookie authentication options
builder.Services.Configure<CookieAuthenticationOptions>(
CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.Cookie.Name = "MyApp.Auth";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.ExpireTimeSpan = TimeSpan.FromHours(1);
options.SlidingExpiration = true;
});
Personalización de controladores de eventos
La autenticación OpenID Connect y JWT Bearer expone eventos a los que puede conectarse. Microsoft. Identity.Web configura sus propios controladores de eventos, por lo que debe encadenar los controladores personalizados con los existentes para conservar la funcionalidad integrada.
Conservar los controladores existentes
Al agregar controladores de eventos personalizados, guarde y llame primero al controlador existente. En el ejemplo siguiente se muestran los enfoques incorrectos y correctos.
El siguiente código sobrescribe incorrectamente el manejador de Microsoft.Identity.Web:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events.OnTokenValidated = async context =>
{
// Your code - but you LOST the built-in validation!
await Task.CompletedTask;
};
});
El código siguiente se encadena correctamente con el controlador existente:
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
// Call Microsoft.Identity.Web's handler FIRST
await existingOnTokenValidatedHandler(context);
// Then your custom code
// (executes AFTER built-in security checks)
var identity = context.Principal.Identity as ClaimsIdentity;
identity?.AddClaim(new Claim("custom_claim", "custom_value"));
};
});
Aplicación de escenarios de eventos comunes
Adición de reclamaciones personalizadas después de la validación del token
El código siguiente agrega reclamaciones personalizadas al ClaimsPrincipal después de la validación de tokens en una API web. Busca el departamento del usuario desde una base de datos y asigna un rol específico de la aplicación en función del dominio de correo electrónico:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Security.Claims;
builder.Services.Configure<JwtBearerOptions>(
JwtBearerDefaults.AuthenticationScheme,
options =>
{
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
// Preserve built-in validation
await existingHandler(context);
// Add custom claims
var identity = context.Principal.Identity as ClaimsIdentity;
// Example: Add department claim from database
var userObjectId = context.Principal.FindFirst("oid")?.Value;
if (!string.IsNullOrEmpty(userObjectId))
{
var department = await GetUserDepartment(userObjectId);
identity?.AddClaim(new Claim("department", department));
}
// Example: Add application-specific role
var email = context.Principal.FindFirst("email")?.Value;
if (email?.EndsWith("@admin.com") == true)
{
identity?.AddClaim(new Claim(ClaimTypes.Role, "SuperAdmin"));
}
};
});
El código siguiente agrega notificaciones personalizadas en una aplicación web llamando a Microsoft Graph para recuperar datos de perfil de usuario adicionales después de la validación del token:
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
// Preserve built-in processing
await existingHandler(context);
// Call Microsoft Graph to get additional user data
var graphClient = context.HttpContext.RequestServices
.GetRequiredService<GraphServiceClient>();
var user = await graphClient.Me.GetAsync();
var identity = context.Principal.Identity as ClaimsIdentity;
identity?.AddClaim(new Claim("jobTitle", user?.JobTitle ?? ""));
identity?.AddClaim(new Claim("department", user?.Department ?? ""));
};
});
Adición de parámetros de consulta a la solicitud de autorización
El código siguiente agrega parámetros de consulta personalizados a la solicitud de autorización enviada al proveedor de identidades de Microsoft Entra:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
var existingHandler = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = async context =>
{
// Preserve existing behavior
if (existingHandler != null)
{
await existingHandler(context);
}
// Add custom query parameters
context.ProtocolMessage.Parameters.Add("slice", "testslice");
context.ProtocolMessage.Parameters.Add("custom_param", "custom_value");
// Conditional parameters based on request
if (context.HttpContext.Request.Query.ContainsKey("prompt"))
{
context.ProtocolMessage.Prompt = context.HttpContext.Request.Query["prompt"];
}
};
});
Personalización del control de errores de autenticación
El código siguiente controla los errores de autenticación registrando el error y devolviendo una respuesta de error JSON personalizada:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnAuthenticationFailed = async context =>
{
// Log the error
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogError(context.Exception, "Authentication failed");
// Customize error response
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync($$"""
{
"error": "authentication_failed",
"error_description": "{{context.Exception.Message}}"
}
""");
context.HandleResponse(); // Suppress default error handling
};
});
Control del acceso denegado
El código siguiente redirige a los usuarios a una página personalizada cuando deniegan el consentimiento:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnAccessDenied = async context =>
{
// User denied consent
context.Response.Redirect("/Home/AccessDenied");
context.HandleResponse();
await Task.CompletedTask;
};
});
Personalización de la adquisición de tokens
Puede personalizar cómo se adquieren los tokens al llamar a las API de bajada pasando opciones a IDownstreamApi.
Uso de IDownstreamApi con opciones personalizadas
El código siguiente pasa un identificador de correlación y parámetros de consulta adicionales cuando se adquiere un token a través de IDownstreamApi.
using Microsoft.Identity.Abstractions;
public class TodoListController : ControllerBase
{
private readonly IDownstreamApi _downstreamApi;
public TodoListController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
[HttpGet("{id}")]
public async Task<ActionResult> GetTodo(int id, Guid correlationId)
{
var result = await _downstreamApi.GetForUserAsync<Todo>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
// Customize token acquisition
options.TokenAcquisitionOptions = new TokenAcquisitionOptions
{
CorrelationId = correlationId,
ExtraQueryParameters = new Dictionary<string, string>
{
{ "slice", "test_slice" }
}
};
});
return Ok(result);
}
}
Personalizar la interfaz de usuario
Puede controlar dónde llegan los usuarios después del inicio de sesión y cierre de sesión y personalizar la experiencia de cierre de sesión.
Redireccionamiento a una página específica después del inicio de sesión
Use el redirectUri parámetro para enviar usuarios a una página específica después de iniciar sesión:
<!-- Razor view -->
<a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard">Sign In</a>
<!-- Or in controller -->
[HttpGet]
public IActionResult SignInToDashboard()
{
return RedirectToAction("SignIn", "Account", new
{
area = "MicrosoftIdentity",
redirectUri = "/Dashboard"
});
}
Personalización de la página de cierre de sesión
Opción 1: Invalidar la página de Razor
Cree un archivo en Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml con el contenido personalizado:
@page
@model Microsoft.Identity.Web.UI.Areas.MicrosoftIdentity.Pages.Account.SignedOutModel
@{
ViewData["Title"] = "Signed out";
}
<div class="container text-center mt-5">
<h1>You have been signed out</h1>
<p>Thank you for using our application.</p>
<a asp-area="" asp-controller="Home" asp-action="Index" class="btn btn-primary">
Return to Home
</a>
</div>
Opción 2: Redirigir a una página personalizada
El código siguiente redirige a los usuarios a una página de cierre de sesión personalizada en lugar del valor predeterminado:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Personalización de la experiencia de inicio de sesión
Uso de sugerencias de inicio de sesión y sugerencias de dominio
Optimice la experiencia de inicio de sesión rellenando previamente los nombres de usuario y dirija a los usuarios a inquilinos de Microsoft Entra específicos.
Entender las sugerencias
| Sugerencia | propósito | Ejemplo |
|---|---|---|
| loginHint | Rellenar previamente el campo nombre de usuario o correo electrónico | "user@contoso.com" |
| domainHint | Redirigir a la página de inicio de sesión de un inquilino específico. | "contoso.com" |
Aplicar patrones de sugerencia
Patrón 1: basado en controlador
En el código siguiente se muestran las acciones del controlador para el inicio de sesión estándar, el inicio de sesión con una sugerencia de inicio de sesión, una sugerencia de dominio o ambas:
using Microsoft.AspNetCore.Mvc;
public class AuthController : Controller
{
[HttpGet]
public IActionResult SignIn()
{
// Standard sign-in
return RedirectToAction("SignIn", "Account", new
{
area = "MicrosoftIdentity",
redirectUri = "/Dashboard"
});
}
[HttpGet]
public IActionResult SignInWithLoginHint()
{
// Pre-populate username
return RedirectToAction("SignIn", "Account", new
{
area = "MicrosoftIdentity",
redirectUri = "/Dashboard",
loginHint = "user@contoso.com"
});
}
[HttpGet]
public IActionResult SignInWithDomainHint()
{
// Direct to Contoso tenant
return RedirectToAction("SignIn", "Account", new
{
area = "MicrosoftIdentity",
redirectUri = "/Dashboard",
domainHint = "contoso.com"
});
}
[HttpGet]
public IActionResult SignInWithBothHints()
{
// Pre-populate AND direct to tenant
return RedirectToAction("SignIn", "Account", new
{
area = "MicrosoftIdentity",
redirectUri = "/Dashboard",
loginHint = "user@contoso.com",
domainHint = "contoso.com"
});
}
}
Patrón 2: basado en vistas
En el código HTML siguiente se muestran vínculos de inicio de sesión con diferentes configuraciones de sugerencias:
<div class="sign-in-options">
<h2>Sign In Options</h2>
<!-- Standard sign-in -->
<a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard"
class="btn btn-primary">
Sign In
</a>
<!-- With login hint -->
<a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard&loginHint=user@contoso.com"
class="btn btn-secondary">
Sign In as user@contoso.com
</a>
<!-- With domain hint -->
<a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard&domainHint=contoso.com"
class="btn btn-secondary">
Sign In (Contoso)
</a>
</div>
Patrón 3: Programación con OnRedirectToIdentityProvider
El código siguiente establece de forma dinámica sugerencias basadas en parámetros de consulta y cookies durante la redirección al proveedor de identidades:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
var existingHandler = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = async context =>
{
if (existingHandler != null)
{
await existingHandler(context);
}
// Add hints based on application logic
if (context.HttpContext.Request.Query.TryGetValue("tenant", out var tenant))
{
context.ProtocolMessage.DomainHint = tenant;
}
// Get suggested user from cookie or session
var suggestedUser = context.HttpContext.Request.Cookies["LastSignedInUser"];
if (!string.IsNullOrEmpty(suggestedUser))
{
context.ProtocolMessage.LoginHint = suggestedUser;
}
};
});
Casos de uso
Plataforma de comercio electrónico:
// Pre-fill returning customer email
loginHint = customerEmail
Aplicación B2B:
// Direct to customer's tenant
domainHint = customerDomain
SaaS multiusuario:
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Seguimiento de los procedimientos recomendados
Qué hacer
1. Conserve siempre los controladores de eventos existentes. Guarde y llame al controlador existente antes de ejecutar la lógica personalizada:
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Use identificadores de correlación para el seguimiento. Adjunte un identificador de correlación a las solicitudes de adquisición de tokens para diagnósticos:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Valide las reclamaciones personalizadas. Compruebe que las declaraciones personalizadas contienen valores esperados antes de conceder acceso:
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Errores de personalización del registro. Encapsular la lógica personalizada en bloques try-catch y registrar errores:
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Pruebe tanto los caminos de éxito como los de fallo. Cubre todos los escenarios de autenticación en tus pruebas.
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Cosas que no debes hacer
1. No omitas los controladores de eventos de Microsoft.Identity.Web:
// Wrong - loses built-in security checks
options.Events.OnTokenValidated = async context => { /* your code */ };
// Correct - preserves security
var existing = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existing(context);
/* your code */
};
2. No habilite el registro de PII en producción:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. No omita la validación de tokens:
// Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;
// Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
4. No codifique de forma rígida los valores confidenciales:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. No modifique la autenticación en middleware:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Solucionar problemas comunes
Solucionar la falta de aplicación de la personalización
Compruebe el orden de ejecución:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApiestablece los valores predeterminados - Tus
Configurellamadas se ejecutan -
PostConfigurellamadas que se ejecutan (si las hay) - Se usan opciones
Solución: Use PostConfigure si la Configure llamada no surte efecto, porque PostConfigure se ejecuta después de todas las Configure llamadas:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Corregir los atributos personalizados que faltan
Compruebe lo siguiente si las declaraciones personalizadas no aparecen:
- El
OnTokenValidatedcontrolador se encadena correctamente con el controlador existente. - La autenticación se realiza correctamente antes de que el código agregue declaraciones.
- Las reclamaciones se agregan al correcto
ClaimsIdentity.
El código siguiente registra todas las afirmaciones para la depuración.
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Corregir eventos que no se activan
Si los eventos no se activan, verifique que los middlewares de autenticación y autorización estén registrados en el orden correcto:
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints