Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Microsoft. Identity.Web fournit des valeurs par défaut sécurisées pour l’authentification et l’autorisation dans ASP.NET Core applications qui s’intègrent à Microsoft Entra ID. Vous pouvez personnaliser de nombreux aspects du comportement d’authentification tout en préservant les fonctionnalités de sécurité intégrées de la bibliothèque.
Identifier les zones personnalisables
| Domaine | Options de personnalisation |
|---|---|
| Configuration | Toutes MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions propriétés |
| Événements | Événements OpenID Connect (OnTokenValidated, OnRedirectToIdentityProvider, etc.) |
| Acquisition de jetons | ID de corrélation, paramètres de requête supplémentaires |
| Revendications | Ajouter des revendications personnalisées à ClaimsPrincipal |
| Interface Utilisateur (UI) | Pages de déconnexion, comportement de redirection |
| Connexion | Indicateurs de connexion, indicateurs de domaine |
Choisir une méthode de personnalisation
Le tableau suivant récapitule les zones que vous pouvez personnaliser et ce que chaque zone prend en charge.
Utilisez l’une des deux approches pour personnaliser les options :
-
Configure<TOptions>- Configure les options avant qu’elles ne soient utilisées -
PostConfigure<TOptions>- Configure les options après tous lesConfigureappels
Ordre d’exécution :
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Configurer les options d’authentification
Cette section montre comment configurer les différentes classes d’option d’authentification qui Microsoft. Identity.Web utilise.
Comprendre le mappage de configuration
La section "AzureAd" dans appsettings.json se mappe à plusieurs classes.
Vous pouvez utiliser n’importe quelle propriété de ces classes dans votre configuration.
Modèle 1 : Configurer MicrosoftIdentityOptions
Le code suivant est personnalisé pour activer la journalisation des informations personnelles, définir les fonctionnalités du client et ajuster les paramètres de validation des jetons MicrosoftIdentityOptions :
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();
Modèle 2 : Configurer OpenIdConnectOptions (applications web)
Le code suivant personnalise OpenIdConnectOptions pour une application web afin de définir le type de réponse, d’ajouter des scopes et de configurer les paramètres de validation des cookies et des jetons.
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;
});
Modèle 3 : Configurer JwtBearerOptions (API web)
Le code suivant personnalise JwtBearerOptions pour une API web afin de définir des audiences valides, des mappages de revendications et une validation valide de la durée de vie des jetons :
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
});
Modèle 4 : Configurer les options de cookie
Le code suivant configure les options d’authentification des cookies et de stratégie de cookie pour votre application, notamment les paramètres de sécurité et le comportement d’expiration :
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;
});
Personnaliser les gestionnaires d’événements
L’authentification OpenID Connect et JWT Bearer exposent les événements auxquels vous pouvez vous connecter. Microsoft. Identity.Web configure ses propres gestionnaires d’événements. Vous devez donc chaîner vos gestionnaires personnalisés avec les gestionnaires existants pour conserver les fonctionnalités intégrées.
Conserver les gestionnaires existants
Lorsque vous ajoutez des gestionnaires d’événements personnalisés, enregistrez toujours et appelez d’abord le gestionnaire existant. L’exemple suivant montre les approches incorrectes et correctes.
Le code suivant incorrectement remplace le gestionnaire 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;
};
});
Le code suivant enchaîne correctement au gestionnaire existant :
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"));
};
});
Appliquer des scénarios d’événements courants
Ajouter des revendications personnalisées après la validation du jeton
Le code suivant ajoute des claims personnalisés à ClaimsPrincipal après la validation du jeton dans une API web. Il recherche le service de l’utilisateur à partir d’une base de données et attribue un rôle spécifique à l’application en fonction du domaine de messagerie :
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"));
}
};
});
Le code suivant ajoute des revendications personnalisées dans une application web en appelant Microsoft Graph pour récupérer des données de profil utilisateur supplémentaires après la validation du jeton :
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 ?? ""));
};
});
Ajouter des paramètres de requête à la demande d’autorisation
Le code suivant ajoute des paramètres de requête personnalisés à la demande d’autorisation envoyée au fournisseur d’identité 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"];
}
};
});
Personnaliser la gestion des échecs d’authentification
Le code suivant gère les échecs d’authentification en journalisant l’erreur et en retournant une réponse d’erreur JSON personnalisée :
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
};
});
Gérer l’accès refusé
Le code suivant redirige les utilisateurs vers une page personnalisée lorsqu’ils refusent le consentement :
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;
};
});
Personnaliser l’acquisition de jetons
Vous pouvez personnaliser la façon dont les jetons sont acquis lors de l’appel d’API en aval en passant des options à IDownstreamApi.
Utiliser IDownstreamApi avec des options personnalisées
Le code suivant transmet un ID de corrélation et des paramètres de requête supplémentaires lors de l’acquisition d’un jeton via 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);
}
}
Personnaliser l’interface utilisateur
Vous pouvez contrôler l’emplacement des utilisateurs après la connexion et la déconnexion, et personnaliser l’expérience de déconnexion.
Rediriger vers une page spécifique après la connexion
Utilisez le redirectUri paramètre pour envoyer des utilisateurs à une page spécifique après leur connexion :
<!-- 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"
});
}
Personnaliser la page de déconnexion
Option 1 : Remplacer la page Razor
Créez un fichier à l'emplacement Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml avec votre contenu personnalisé :
@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>
Option 2 : Rediriger vers une page personnalisée
Le code suivant redirige les utilisateurs vers une page de déconnexion personnalisée au lieu de la valeur par défaut :
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Personnaliser l’expérience de connexion
Utiliser des indicateurs de connexion et des indicateurs de domaine
Simplifiez l’expérience de connexion en préremplissant les noms d’utilisateur et en dirigeant les utilisateurs vers des locataires Microsoft Entra spécifiques.
Comprendre les indicateurs
| Indicateur | Objectif | Exemple |
|---|---|---|
| loginHint | Préremplir le champ nom d’utilisateur/e-mail | "user@contoso.com" |
| domainHint | Diriger vers la page de connexion spécifique du locataire | "contoso.com" |
Appliquer des modèles d'indices
Modèle 1 : basé sur le contrôleur
Le code suivant montre les actions du contrôleur pour la connexion standard, la connexion avec un indicateur de connexion, un indicateur de domaine ou les deux :
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"
});
}
}
Modèle 2 : basé sur l’affichage
Le code HTML suivant montre les liens de connexion avec différentes configurations d’indicateur :
<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>
Modèle 3 : Programmatique avec OnRedirectToIdentityProvider
Le code suivant définit dynamiquement des indicateurs en fonction des paramètres de requête et des cookies pendant la redirection vers le fournisseur d’identité :
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;
}
};
});
Cas d’utilisation
Plateforme de commerce électronique :
// Pre-fill returning customer email
loginHint = customerEmail
Application B2B :
// Direct to customer's tenant
domainHint = customerDomain
SaaS multilocataire :
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Suivre les bonnes pratiques
Choses à faire
1. Conservez toujours les gestionnaires d’événements existants. Enregistrez et appelez le gestionnaire existant avant d’exécuter votre logique personnalisée :
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Utilisez les ID de corrélation pour le suivi. Attachez un ID de corrélation aux demandes d’acquisition de jetons pour les diagnostics :
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Valider les revendications personnalisées. Vérifiez que les revendications personnalisées contiennent des valeurs attendues avant d’accorder l’accès :
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Journaliser les erreurs de personnalisation. Encapsuler la logique personnalisée dans des blocs try-catch et enregistrer les erreurs.
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Testez les chemins de réussite et d’échec. Couvrez tous les scénarios d’authentification dans vos tests :
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Choses à ne pas faire
1. Ne passez pas à côté des gestionnaires d'événements 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. N’activez pas la journalisation des informations personnelles en production :
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Ne pas contourner la validation des jetons :
// Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;
// Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
4. Ne codez pas en dur les valeurs sensibles :
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Ne modifiez pas l’authentification dans le middleware :
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Résoudre les problèmes courants
Résoudre le problème de personnalisation qui ne prend pas effet
Vérifier l’ordre d’exécution :
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApidéfinit les valeurs par défaut - Vos
Configureappels sont en cours -
PostConfigureappels exécutés (le cas échéant) - Les options sont utilisées
Solution: Utilisez PostConfigure si votre Configure appel ne prend pas effet, car PostConfigure s’exécute après tous les Configure appels :
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Corriger les revendications personnalisées manquantes
Vérifiez ce qui suit si les revendications personnalisées n’apparaissent pas :
- Le
OnTokenValidatedgestionnaire est chaîné correctement avec le gestionnaire existant. - L’authentification réussit avant que votre code ajoute des revendications.
- Les réclamations sont ajoutées au
ClaimsIdentitycorrect.
Le code suivant consigne toutes les demandes à des fins de débogage :
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Correction des événements qui ne se déclenchent pas
Si les événements ne se déclenchent pas, vérifiez que le middleware d'authentification et d'autorisation sont enregistrés dans l'ordre correct :
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints