Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Microsoft. Identity.Web предоставляет безопасные значения по умолчанию для проверки подлинности и авторизации в ASP.NET Core приложениях, которые интегрируются с Microsoft Entra ID. При сохранении встроенных функций безопасности библиотеки можно настроить множество аспектов поведения проверки подлинности.
Определение настраиваемых областей
| Area | Параметры настройки |
|---|---|
| Configuration | Все MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions свойства |
| События | События OpenID Connect (OnTokenValidated, OnRedirectToIdentityProviderи т. д.) |
| Получение токена | Идентификаторы корреляции, дополнительные параметры запроса |
| Утверждения | Добавление настраиваемых утверждений в ClaimsPrincipal |
| Пользовательский интерфейс | Страницы выхода из системы, поведение при перенаправлении |
| Вход | Подсказки для входа, указания домена |
Выбор метода настройки
В следующей таблице перечислены области, которые можно настроить и какие области поддерживаются.
Используйте один из двух подходов для настройки параметров:
-
Configure<TOptions>— настраивает параметры перед их применением -
PostConfigure<TOptions>— настраивает параметры после всехConfigureвызовов
Порядок выполнения:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Настройка параметров проверки подлинности
В этом разделе показано, как настроить различные классы параметров проверки подлинности, которые Microsoft. Identity.Web используется.
Понимание сопоставления конфигурации
Раздел "AzureAd" в appsettings.json отображается на несколько классов.
Вы можете использовать любое свойство из этих классов в конфигурации.
Шаблон 1. Настройка MicrosoftIdentityOptions
Следующий код настраивает MicrosoftIdentityOptions для включения ведения журнала PII, установки возможностей клиента и корректировки параметров проверки токена:
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();
Шаблон 2. Настройка OpenIdConnectOptions (веб-приложения)
Следующий код настраивает компонент OpenIdConnectOptions в веб-приложении, чтобы задать тип ответа, добавить области доступа и настроить параметры проверки файлов cookie и маркеров.
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;
});
Шаблон 3. Настройка JwtBearerOptions (веб-API)
Следующий код настраивает JwtBearerOptions веб-API для задания допустимых аудиторий, сопоставлений утверждений и проверки времени существования токена:
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
});
Шаблон 4. Настройка параметров cookie
Следующий код настраивает параметры политики cookie и проверки подлинности файлов cookie для приложения, включая параметры безопасности и поведение срока действия:
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;
});
Настройка обработчиков событий
Аутентификация OpenID Connect и JWT Bearer раскрывает события, к которым можно подключиться. Microsoft.Identity.Web настраивает собственные обработчики событий, поэтому необходимо связать пользовательские обработчики с существующими для сохранения встроенного функционала.
Сохраните существующие обработчики
При добавлении пользовательских обработчиков событий всегда сохраняйте и вызывайте существующий обработчик. В следующем примере показаны неправильные и правильные подходы.
Следующий код неверно перезаписывает обработчик 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;
};
});
Следующий код правильно цепляется к существующему обработчику.
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"));
};
});
Применение распространенных сценариев событий
Добавление пользовательских утверждений после проверки токена
Следующий код добавляет пользовательские утверждения в ClaimsPrincipal после проверки токена в веб-API. Он ищет отдел пользователя из базы данных и назначает роль для конкретного приложения на основе домена электронной почты:
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"));
}
};
});
Следующий код добавляет пользовательские утверждения в веб-приложении с использованием Microsoft Graph для получения дополнительных данных профиля пользователя после проверки токена.
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 ?? ""));
};
});
Добавление параметров запроса в запрос авторизации
Следующий код добавляет настраиваемые параметры запроса к запросу авторизации, отправленного поставщику удостоверений 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"];
}
};
});
Настройка обработки сбоев проверки подлинности
Следующий код обрабатывает сбои проверки подлинности, регистрируя ошибку и возвращая пользовательский ответ на ошибку JSON:
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
};
});
Обработка отказа в доступе
Следующий код перенаправляет пользователей на пользовательскую страницу при отказе согласия:
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;
};
});
Настройка получения токена
Вы можете настроить способ получения токенов при вызове API на более низком уровне, передав параметры в IDownstreamApi.
Использование IDownstreamApi с настраиваемыми параметрами
Следующий код передает идентификатор корреляции и дополнительные параметры запроса при получении токена через 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);
}
}
Настройка пользовательского интерфейса
Вы можете контролировать, куда пользователи перенаправляются после входа и выхода, а также настроить опыт после выхода.
Перенаправление на определенную страницу после входа
redirectUri Используйте параметр для отправки пользователей на определенную страницу после входа:
<!-- 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"
});
}
Настройка страницы выхода
Вариант 1. Переопределение страницы Razor
Создайте файл Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml с вашим собственным содержимым:
@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>
Вариант 2. Перенаправление на настраиваемую страницу
Следующий код перенаправляет пользователей на пользовательскую страницу выхода вместо стандартной:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Настройка интерфейса входа
Использование подсказок для входа и подсказок домена
Упрощение процесса входа настройкой автозаполнения имен пользователей и перенаправлением пользователей к определенным арендаторам Microsoft Entra.
Понимание подсказок
| Hint | Purpose | Пример |
|---|---|---|
| loginHint | Предварительное заполнение поля имени пользователя и/или электронной почты | "user@contoso.com" |
| domainHint | Переход на страницу входа для конкретного арендатора | "contoso.com" |
Применение шаблонов подсказок
Шаблон 1. На основе контроллера
В следующем коде показаны действия контроллера для стандартного входа, входа с подсказкой для имени пользователя, с подсказкой для домена или с обоими подсказками.
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"
});
}
}
Шаблон 2. Представление
В следующем HTML-коде показаны ссылки на вход с различными конфигурациями подсказок:
<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>
Шаблон 3: Программный шаблон с OnRedirectToIdentityProvider
Следующий код динамически задает указания на основе параметров запроса и файлов cookie во время перенаправления на поставщика удостоверений:
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;
}
};
});
Случаи использования
Платформа электронной коммерции:
// Pre-fill returning customer email
loginHint = customerEmail
Приложение B2B:
// Direct to customer's tenant
domainHint = customerDomain
Многопользовательский SaaS (ПО как услуга):
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Следуйте лучшим практикам
Что нужно делать
1. Всегда сохранять существующие обработчики событий. Сохраните и вызовите существующий обработчик перед выполнением пользовательской логики:
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Используйте идентификаторы корреляции для трассировки. Присоединение идентификатора корреляции к запросам на получение токенов для диагностики:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Проверка пользовательских утверждений. Убедитесь, что пользовательские утверждения содержат ожидаемые значения перед предоставлением доступа:
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Ошибки настройки журнала. Оборачивайте пользовательскую логику в блоки try-catch и логируйте ошибки.
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Проверьте пути успешности и сбоя. Охватывайте все сценарии проверки подлинности в тестах:
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Чего не следует делать
1. Не пропускайте обработчики событий 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. Не включайте ведение журнала PII в рабочей среде:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Не обходить проверку маркера:
// Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;
// Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
4. Не хардкодируйте конфиденциальные значения:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Не изменяйте проверку подлинности в ПО промежуточного слоя:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Устранение распространенных неполадок
Устранение проблемы с настройками, которые не применяются
Проверьте порядок выполнения:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApiзадает значения по умолчанию - Ваши
Configureвызовы выполняются -
PostConfigureвызовы выполняются (если имеются) - Параметры используются
Решение: Используйте, PostConfigure если вызов Configure не действует, так как PostConfigure выполняется после всех Configure вызовов:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Исправление отсутствующих пользовательских утверждений
Проверьте следующее, если пользовательские утверждения не отображаются:
- Обработчик
OnTokenValidatedправильно связан с существующим обработчиком. - Проверка подлинности завершается успешно, прежде чем код добавляет утверждения.
- Утверждения добавляются в правильные
ClaimsIdentity.
В следующем коде регистрируются все утверждения для отладки:
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Исправление событий, которые не запускаются
Если события не запускаются, убедитесь, что промежуточное ПО для аутентификации и авторизации зарегистрировано в правильном порядке.
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints