Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Microsoft. Identity.Web offre impostazioni predefinite sicure per l'autenticazione e l'autorizzazione nelle applicazioni ASP.NET Core che si integrano con Microsoft Entra ID. È possibile personalizzare molti aspetti del comportamento di autenticazione mantenendo al tempo stesso le funzionalità di sicurezza predefinite della libreria.
Identificare le aree personalizzabili
| Area | Opzioni di personalizzazione |
|---|---|
| Configuration | Tutte le MicrosoftIdentityOptions, OpenIdConnectOptions e JwtBearerOptions proprietà |
| Events | Eventi OpenID Connect (OnTokenValidated, OnRedirectToIdentityProvidere così via) |
| Acquisizione di token | ID di correlazione, parametri di query aggiuntivi |
| Claims | Aggiungere attestazioni personalizzate a ClaimsPrincipal |
| UI | Pagine di disconnessione, comportamento di reindirizzamento |
| Accesso | Suggerimenti di login, suggerimenti di dominio |
Scegliere un metodo di personalizzazione
La tabella seguente riepiloga le aree che è possibile personalizzare e ciò che ogni area supporta.
Usare uno dei due approcci per personalizzare le opzioni:
-
Configure<TOptions>- Configura le opzioni prima di essere usate -
PostConfigure<TOptions>- Configura le opzioni dopo tutte leConfigurechiamate
Ordine di esecuzione:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Configurare le opzioni di autenticazione
Questa sezione illustra come configurare le varie classi di opzioni di autenticazione Microsoft. Identity.Web usa .
Comprendere il mapping della configurazione
La sezione in "AzureAd"appsettings.json mappa a più classi:
È possibile usare qualsiasi proprietà di queste classi nella configurazione.
Modello 1: Configurare MicrosoftIdentityOptions
Il codice seguente personalizza MicrosoftIdentityOptions per consentire il log delle informazioni personali (PII), impostare le capacità del client e regolare i parametri di convalida dei token.
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();
Modello 2: Configurare OpenIdConnectOptions (app Web)
Il codice seguente consente di personalizzare OpenIdConnectOptions per un'app Web per impostare il tipo di risposta, aggiungere ambiti e configurare le impostazioni di convalida dei cookie e dei token:
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;
});
Modello 3: Configurare JwtBearerOptions (API Web)
Il codice seguente personalizza JwtBearerOptions per un'API Web impostando destinatari validi, mappature di attestazioni e convalida della durata dei 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
});
Modello 4: Configurare le opzioni dei cookie
Il codice seguente configura i criteri di cookie e le opzioni di autenticazione dei cookie per l'app, incluse le impostazioni di sicurezza e il comportamento di scadenza:
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;
});
Personalizzare i gestori eventi
L'autenticazione OpenID Connect e JWT Bearer espongono eventi a cui è possibile collegarsi. Microsoft. Identity.Web configura i propri gestori eventi, quindi è necessario concatenare i gestori personalizzati con quelli esistenti per mantenere le funzionalità predefinite.
Mantenere i gestori esistenti
Quando si aggiungono gestori eventi personalizzati, salvare e chiamare prima il gestore esistente. Nell'esempio seguente vengono illustrati gli approcci errati e corretti.
Il codice seguente in modo non corretto sovrascrive il gestore di 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;
};
});
Il codice seguente correttamente collega al gestore esistente:
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"));
};
});
Applicare scenari di eventi comuni
Aggiungere attestazioni personalizzate dopo la convalida del token
Il codice seguente aggiunge attestazioni personalizzate al ClaimsPrincipal dopo la convalida del token in un'API web. Cerca il reparto dell'utente da un database e assegna un ruolo specifico dell'applicazione in base al dominio di posta elettronica:
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"));
}
};
});
Il codice seguente aggiunge attestazioni personalizzate in un'app Web chiamando Microsoft Graph per recuperare dati aggiuntivi del profilo utente dopo la convalida 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 ?? ""));
};
});
Aggiungere parametri di query alla richiesta di autorizzazione
Il codice seguente aggiunge parametri di query personalizzati alla richiesta di autorizzazione inviata al provider di 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"];
}
};
});
Personalizzare la gestione degli errori di autenticazione
Il codice seguente gestisce gli errori di autenticazione registrando l'errore e restituendo una risposta di errore JSON personalizzata:
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
};
});
Gestione dell'accesso negato
Il codice seguente reindirizza gli utenti a una pagina personalizzata quando negano il consenso:
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;
};
});
Personalizzare l'acquisizione di token
È possibile personalizzare la modalità di acquisizione dei token quando si chiamano le API downstream passando le opzioni a IDownstreamApi.
Usare IDownstreamApi con opzioni personalizzate
Il codice seguente passa un ID di correlazione e parametri di query aggiuntivi durante l'acquisizione di un token tramite 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);
}
}
Personalizzare l'interfaccia utente
È possibile controllare la pagina a cui gli utenti vengono indirizzati dopo l'accesso e la disconnessione e personalizzare l'esperienza dopo la disconnessione.
Reindirizzare a una pagina specifica dopo l'accesso
Usare il redirectUri parametro per inviare gli utenti a una pagina specifica dopo l'accesso:
<!-- 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"
});
}
Personalizzare la pagina di disconnessione
Opzione 1: Eseguire l'override della pagina Razor
Creare un file in Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml con il contenuto personalizzato:
@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>
Opzione 2: Reindirizzare a una pagina personalizzata
Il codice seguente reindirizza gli utenti a una pagina di disconnessione personalizzata anziché all'impostazione predefinita:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Personalizzare l'esperienza di accesso
Usare hint di accesso e hint di dominio
Ottimizzare l'esperienza di accesso popolando automaticamente i nomi utente e indirizzando gli utenti verso tenant specifici di Microsoft Entra.
Comprendere i suggerimenti
| Suggerimento | Scopo | Esempio |
|---|---|---|
| loginHint | Precompilare il campo nome utente/indirizzo di posta elettronica | "user@contoso.com" |
| domainHint | Pagina di accesso diretto a un tenant specifico | "contoso.com" |
Applicare modelli di hint
Modello 1: basato su controller
Il codice seguente mostra le azioni del controller per l'accesso standard, l'accesso con un hint di accesso, un hint di dominio o entrambi:
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"
});
}
}
Modello 2: basato sulla visualizzazione
Il codice HTML seguente mostra i collegamenti di accesso con configurazioni di hint diverse:
<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>
Modello 3: a livello di codice con OnRedirectToIdentityProvider
Il codice seguente imposta in modo dinamico gli hint in base ai parametri di query e ai cookie durante il reindirizzamento al provider di 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;
}
};
});
Casi d'uso
Piattaforma di e-commerce:
// Pre-fill returning customer email
loginHint = customerEmail
Applicazione B2B:
// Direct to customer's tenant
domainHint = customerDomain
SaaS multi-tenant:
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Seguire le migliori pratiche
Cose da fare
1. Mantenere sempre i gestori eventi esistenti. Salvare e chiamare il gestore esistente prima di eseguire la logica personalizzata:
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Usare gli ID di correlazione per il tracciamento. Associare un ID di correlazione alle richieste di acquisizione di token per la diagnostica:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Convalidare le attestazioni personalizzate. Verificare che le attestazioni personalizzate contengano valori previsti prima di concedere l'accesso:
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Registrare gli errori di personalizzazione. Avvolgere la logica personalizzata in blocchi try-catch e registrare gli errori:
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Testare sia i percorsi di esito positivo che quello di errore. Coprire tutti gli scenari di autenticazione nei test:
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Cose da non fare
1. Non saltare i gestori di eventi di 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. Non abilitare la registrazione delle informazioni personali nell'ambiente di produzione:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Non ignorare la convalida dei token:
// Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;
// Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
4. Non codificare valori sensibili:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Non modificare l'autenticazione nel middleware:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Risolvere i problemi comuni
Risolvere la mancata applicazione della personalizzazione
Controllare l'ordine di esecuzione:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApiimposta le impostazioni predefinite - Le
Configurechiamate vengono eseguite -
PostConfigurechiamate eseguite (se presenti) - Vengono usate le opzioni
Soluzione: Usare PostConfigure se la Configure chiamata non ha effetto, perché PostConfigure viene eseguita dopo tutte le Configure chiamate:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Correzione di attestazioni personalizzate mancanti
Verificare quanto segue se le attestazioni personalizzate non vengono visualizzate:
- Il
OnTokenValidatedgestore viene concatenato correttamente con il gestore esistente. - L'autenticazione ha esito positivo prima che il codice aggiunga i claim.
- Le dichiarazioni vengono aggiunte all'entità corretta
ClaimsIdentity.
Il codice seguente registra tutte le attestazioni per il debug:
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Correzione degli eventi non attivati
Se gli eventi non si attivano, verificare che il middleware di autenticazione e quello di autorizzazione siano registrati nell'ordine corretto:
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints