Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Microsoft. O Identity.Web fornece predefinições seguras para autenticação e autorização em aplicações ASP.NET Core que se integram com o Microsoft Entra ID. Pode personalizar muitos aspetos do comportamento de autenticação enquanto preserva as funcionalidades de segurança integradas da biblioteca.
Identificar áreas personalizáveis
| Area | Opções de Personalização |
|---|---|
| Configuração | Todas MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions propriedades |
| Events | Eventos OpenID Connect (OnTokenValidated, OnRedirectToIdentityProvider, etc.) |
| Aquisição de Tokens | IDs de correlação, parâmetros extra de consulta |
| Claims | Adicionar reivindicações personalizadas a ClaimsPrincipal |
| UI(Interface de Utilizador) | Páginas de encerramento de sessão, comportamento de redirecionamento |
| Iniciar sessão | Dicas de login, dicas de domínio |
Escolha um método de personalização
A tabela seguinte resume as áreas que pode personalizar e o que cada área suporta.
Use uma de duas abordagens para personalizar as opções:
-
Configure<TOptions>- Configura as opções antes de serem usadas -
PostConfigure<TOptions>- Configura opções após todas asConfigurechamadas
Ordem de execução:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Configurar opções de autenticação
Esta secção mostra como configurar as várias classes de opções de autenticação que a Microsoft. O Identity.Web utiliza.
Compreender o mapeamento de configuração
A "AzureAd" secção em appsettings.json corresponde a múltiplas classes:
Pode usar qualquer propriedade destas classes na sua configuração.
Padrão 1: Configurar MicrosoftIdentityOptions
O seguinte código personaliza MicrosoftIdentityOptions para permitir o registo de PII, definir capacidades do cliente e ajustar parâmetros de validação 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();
Padrão 2: Configurar OpenIdConnectOptions (aplicações Web)
O código seguinte personaliza OpenIdConnectOptions uma aplicação web para definir o tipo de resposta, adicionar escopos e configurar as definições de validação de cookies e 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;
});
Padrão 3: Configurar JwtBearerOptions (APIs Web)
O seguinte código personaliza JwtBearerOptions uma API web para definir audiências válidas, mapeamentos de reivindicações e validação vitalícia do 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
});
Padrão 4: Configurar opções de cookies
O seguinte código configura a política de cookies e as opções de autenticação de cookies para a sua aplicação, incluindo definições de segurança e comportamento de validade:
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;
});
Personalizar os gestores de eventos
O OpenID Connect e a autenticação JWT Bearer expõem eventos aos quais você pode se conectar. Microsoft.Identity.Web configura os seus próprios manipuladores de eventos, por isso deve encadear os seus manipuladores existentes com os personalizados para preservar a funcionalidade incorporada.
Preservar os manipuladores existentes
Quando adicionas gestores de eventos personalizados, guarda sempre e chama primeiro o gestor existente. O exemplo seguinte mostra as abordagens erradas e corretas.
O código seguinte incorretamente sobrescreve o handler 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;
};
});
O código seguinte encadeia corretamente com o handler 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"));
};
});
Aplicar cenários de eventos comuns
Adicionar reivindicações personalizadas após validação do token
O código seguinte adiciona reivindicações personalizadas ao ClaimsPrincipal após a validação do token numa API web. Procura o departamento do utilizador a partir de uma base de dados e atribui um papel específico da aplicação com base no domínio de email:
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"));
}
};
});
O seguinte código adiciona reivindicações personalizadas numa aplicação web ao ligar para a Microsoft Graph para recuperar dados adicionais de perfil de utilizador após a validação do 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 ?? ""));
};
});
Adicionar parâmetros de consulta ao pedido de autorização
O seguinte código adiciona parâmetros personalizados de consulta ao pedido de autorização enviado ao fornecedor de identidade da 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"];
}
};
});
Personalizar o tratamento de falhas de autenticação
O código seguinte trata das falhas de autenticação registando o erro e retornando uma resposta personalizada de erro 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
};
});
Acesso ao handle negado
O seguinte código redireciona os utilizadores para uma página personalizada quando recusam o consentimento:
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;
};
});
Personalizar a aquisição de tokens
Pode personalizar a forma como os tokens são adquiridos ao chamar APIs a jusante passando opções para IDownstreamApi.
Use o IDownstreamApi com opções personalizadas
O código seguinte passa um ID de correlação e parâmetros adicionais de consulta ao adquirir um token atravé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 a interface do usuário
Pode controlar onde os utilizadores são direcionados após o início e o término de sessão e personalizar a experiência após o término de sessão.
Redirecionar para uma página específica após iniciar sessão
Use o redirectUri parâmetro para enviar os utilizadores para uma página específica depois de iniciarem sessão:
<!-- 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"
});
}
Personalizar a página de logout
Opção 1: Sobrepor a página do Razor
Crie um ficheiro em Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml com o seu conteúdo 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>
Opção 2: Redirecionar para uma página personalizada
O código seguinte redireciona os utilizadores para uma página personalizada de saída em vez da predefinida:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Personalize a experiência de início de sessão
Usa dicas de login e dicas de domínio
Simplifique a experiência de início de sessão pré-preenchendo nomes de utilizador e direcionando os utilizadores para tenants específicos do Microsoft Entra.
Compreender as pistas
| Sugestão | Purpose | Exemplo |
|---|---|---|
| loginHint | Prepreencher o campo de nome de utilizador/email | "user@contoso.com" |
| domainHint | Página de login direta para inquilinos específicos | "contoso.com" |
Aplicar padrões de dicas
Padrão 1: Baseado em controlador
O código seguinte mostra as ações do controlador para iniciar sessão padrão, iniciar sessão com uma dica de login, dica de domínio, ou ambos:
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"
});
}
}
Padrão 2: Baseado em visualizações
O seguinte HTML mostra links de início de sessão com diferentes configurações de dicas:
<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>
Padrão 3: Programático com OnRedirectToIdentityProvider
O seguinte código define dinamicamente pistas com base em parâmetros de consulta e cookies durante o redirecionamento para o fornecedor de identidade:
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 Comércio Eletrónico:
// Pre-fill returning customer email
loginHint = customerEmail
Aplicação B2B:
// Direct to customer's tenant
domainHint = customerDomain
SaaS Multi-Inquilino:
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Siga as melhores práticas
Coisas a Fazer
1. Preserve sempre os gestores de eventos existentes. Guarde e chame o handler existente antes de executar a sua 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 IDs de correlação para rastreio. Anexe um ID de correlação aos pedidos de aquisição de tokens para diagnóstico:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Validar reivindicações personalizadas. Verifique se as reivindicações personalizadas contêm valores esperados antes de conceder acesso:
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Registar erros de personalização. Envolva a lógica personalizada em blocos try-catch e registe os erros.
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Testar tanto os caminhos de sucesso como de fracasso. Cubra todos os cenários de autenticação nos seus testes:
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
Coisas a não fazer
1. Não ignores os manipuladores de eventos do 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ão ative o registro de PII em produção:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Não ignore a validação do 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. Não codifique de forma fixa valores sensíveis:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Não modifique a autenticação em middleware:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Resolver problemas comuns
A personalização da resolução não tem efeito
Verifique a ordem de execução:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApiDefine os padrões - As tuas
Configurechamadas funcionam -
PostConfigureChamadas em curso (se houver) - São usadas opções
Solução: Utilize PostConfigure se a sua chamada Configure não estiver a ter efeito, porque PostConfigure é executada após todas as chamadas Configure:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Corrigir declarações personalizadas em falta
Verifique o seguinte caso não apareçam reclamações personalizadas:
- O
OnTokenValidatedhandler está corretamente encadeado com o handler existente. - A autenticação é bem-sucedida antes de o seu código adicionar declarações.
- As reivindicações são adicionadas ao
ClaimsIdentitycorreto.
O código seguinte regista todas as declarações para depuração:
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Corrigir eventos que não são acionados
Se os eventos não estiverem a disparar, verifique se o middleware de autenticação e autorização está registado na ordem correta:
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints