Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Microsoft. Identity.Web poskytuje zabezpečené výchozí hodnoty pro ověřování a autorizaci v aplikacích ASP.NET Core, které se integrují s Microsoft Entra ID. Při zachování integrovaných funkcí zabezpečení knihovny můžete přizpůsobit mnoho aspektů chování ověřování.
Identifikace přizpůsobitelných oblastí
| Plocha | Možnosti přizpůsobení |
|---|---|
| Configuration | Všechny MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions vlastnosti |
| Události | Události OpenID Connect (OnTokenValidated, OnRedirectToIdentityProvider atd.) |
| Získání tokenu | ID korelace, další parametry dotazu |
| Claims | Přidat vlastní nároky do ClaimsPrincipal |
| Uživatelské rozhraní | Odhlášení stránek, chování přesměrování |
| Přihlášení | Rady pro přihlášení, nápovědy k doméně |
Volba metody přizpůsobení
Následující tabulka shrnuje oblasti, které můžete přizpůsobit a jaké jednotlivé oblasti podporují.
K přizpůsobení možností použijte jeden ze dvou přístupů:
-
Configure<TOptions>– Konfiguruje možnosti před jejich používáním. -
PostConfigure<TOptions>– Nakonfiguruje možnosti po všechConfigurevoláních.
Pořadí provádění:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Konfigurace možností ověřování
Tato část ukazuje, jak nakonfigurovat různé třídy možností ověřování, které Microsoft. Identity.Web používá.
Principy mapování konfigurace
Sekce "AzureAd" v appsettings.json mapuje do více tříd:
V konfiguraci můžete použít libovolnou vlastnost z těchto tříd.
Vzor 1: Konfigurace MicrosoftIdentityOptions
Následující kód přizpůsobí MicrosoftIdentityOptions aktivaci protokolování PII, nastaví schopnosti klienta a upraví parametry ověřování tokenu:
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();
Vzor 2: Konfigurovat OpenIdConnectOptions (webové aplikace)
Následující kód přizpůsobuje OpenIdConnectOptions pro webovou aplikaci k nastavení typu odpovědi, přidání rozsahů oprávnění a konfiguraci nastavení ověřování cookie a 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;
});
Vzorec 3: Konfigurace JwtBearerOptions (webová rozhraní API)
Následující kód přizpůsobí JwtBearerOptions pro webové API tak, aby nastavil platné publikum, mapování nároků a ověření doby platnosti 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
});
Model 4: Konfigurace možností souborů cookie
Následující kód nakonfiguruje zásady souborů cookie a možnosti ověřování souborů cookie pro vaši aplikaci, včetně nastavení zabezpečení a chování při vypršení platnosti:
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;
});
Přizpůsobení obslužných rutin událostí
Ověřování OpenID Connect a JWT Bearer zveřejňuje události, ke kterým se můžete připojit. Microsoft. Identity.Web nastavuje vlastní obslužné rutiny událostí, takže vlastní obslužné rutiny musíte zřetězovat s existujícími obslužnými rutinami, abyste zachovali předdefinované funkce.
Zachovat současné obsluhy
Když přidáte vlastní obslužné rutiny událostí, vždy nejprve uložte a volejte existující obslužnou rutinu. Následující příklad ukazuje nesprávné a správné přístupy.
Následující kód nesprávně přepíše Microsoft.Identity.Web obslužná rutina.
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events.OnTokenValidated = async context =>
{
// Your code - but you LOST the built-in validation!
await Task.CompletedTask;
};
});
Následující kód správně zřetědí stávající obslužnou rutinu:
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"));
};
});
Použití běžných scénářů událostí
Přidání vlastních deklarací identity po ověření tokenu
Následující kód přidá vlastní deklarace identity do ClaimsPrincipal po ověření tokenu ve webovém rozhraní API. Vyhledá oddělení uživatele z databáze a přiřadí roli specifickou pro aplikaci na základě e-mailové domény:
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"));
}
};
});
Následující kód přidá do webové aplikace vlastní deklarace voláním Microsoft Graph k načtení dalších dat profilu uživatele po validaci tokenu.
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 ?? ""));
};
});
Přidání parametrů dotazu do žádosti o autorizaci
Následující kód přidá vlastní parametry dotazu do žádosti o autorizaci odeslané zprostředkovateli identity 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"];
}
};
});
Přizpůsobte řešení selhání ověřování
Následující kód zpracovává chyby ověřování zaznamenáním chyby a vrácením vlastní JSON odpovědi s chybou.
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
};
});
Řešení odepření přístupu
Následující kód přesměruje uživatele na vlastní stránku při odepření souhlasu:
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;
};
});
Úprava procesu získání tokenu
cs-CZ: Možnost získání tokenů při volání podřízených rozhraní API můžete přizpůsobit předáním možností do IDownstreamApi.
Použití IDownstreamApi s vlastními možnostmi
Následující kód předá ID korelace a další parametry dotazu při získání tokenu prostřednictvím 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);
}
}
Přizpůsobení uživatelského rozhraní
Můžete určit, kde se uživatelé dostanou po přihlášení a odhlášení, a přizpůsobit prostředí odhlášení.
Přesměrování na konkrétní stránku po přihlášení
Pomocí parametru redirectUri odešlete uživatele na konkrétní stránku po přihlášení:
<!-- 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"
});
}
Přizpůsobení stránky odhlášení
Možnost 1: Přepsání stránky Razor Page
Vytvořte soubor Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml s vlastním obsahem:
@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>
Možnost 2: Přesměrování na vlastní stránku
Následující kód přesměruje uživatele na vlastní odhlášenou stránku místo výchozí stránky:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Přizpůsobení přihlašovacího prostředí
Použití tipů pro přihlášení a nápovědy k doméně
Zjednodušte přihlašování tím, že předem vyplníte uživatelská jména a přesměrujete uživatele na konkrétní tenanti Microsoft Entra.
Vysvětlení tipů
| Hint | Purpose | Příklad |
|---|---|---|
| loginHint | Předplnit pole uživatelského jména a e-mailu | "user@contoso.com" |
| domainHint | Přímo na přihlašovací stránku konkrétního tenanta | "contoso.com" |
Použití vzorů nápovědy
Model 1: Založený na kontroleru
Následující kód ukazuje akce kontroleru pro standardní přihlášení, přihlášení pomocí nápovědy pro přihlášení, nápovědu k doméně nebo obojí:
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"
});
}
}
Vzorec 2: Na základě zobrazení
Následující kód HTML zobrazuje přihlašovací odkazy s různými konfiguracemi nápovědy:
<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>
Vzor 3: Programování s OnRedirectToIdentityProvider
Následující kód dynamicky nastavuje rady na základě parametrů dotazu a souborů cookie během přesměrování na zprostředkovatele identity:
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;
}
};
});
Případy použití
Platforma elektronického obchodování:
// Pre-fill returning customer email
loginHint = customerEmail
Aplikace B2B:
// Direct to customer's tenant
domainHint = customerDomain
SaaS s více tenanty:
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Dodržujte osvědčené postupy.
Co dělat
1. Vždy zachovat existující obslužné rutiny událostí. Před spuštěním vlastní logiky uložte a zavolejte existující obslužnou rutinu:
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Pro trasování použijte ID korelace. Připojte ID korelace k žádostem o získání tokenu pro diagnostiku:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Ověřte vlastní nároky. Před udělením přístupu ověřte, že uživatelské nároky obsahují očekávané hodnoty.
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Chyby přizpůsobení logování. Zabalte vlastní logiku do bloků try-catch a zaznamenejte chyby.
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Otestujte cesty úspěchu i selhání. Zahrňte všechny scénáře ověřování ve svých testech.
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Zakázané činnosti
1. Nepřeskočte obslužné rutiny událostí 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. Nepovolujte protokolování PII v produkčním prostředí:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Nepoužívat ověřování 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. Neokódujte citlivé hodnoty:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Neupravujte ověřování v middlewaru:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Řešení běžných potíží
Vyřešit, že přizpůsobení nefunguje.
Zkontrolujte pořadí provádění:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApinastaví výchozí hodnoty. - Vaše
Configurevolání probíhají -
PostConfigurevolání se spustí (pokud existuje) - Používají se možnosti.
Řešení: Použijte PostConfigure , pokud se volání Configure neprojeví, protože PostConfigure se spustí po všech Configure voláních:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Oprava chybějících vlastních nároků
Pokud se vlastní nároky nezobrazí, ověřte následující:
- Obslužná rutina
OnTokenValidatedje správně zřetězená s existující obslužnou rutinou. - Ověřování proběhne úspěšně předtím, než váš kód přidá identifikační nároky.
- Položky jsou přidány ke správnému
ClaimsIdentity.
Následující kód zaznamenává všechny nároky pro ladění.
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Oprava neaktivujících událostí
Pokud se události neaktivují, ověřte, že jsou ověřovací a autorizační middleware zaregistrovány ve správném pořadí.
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints