ITLab.Template.DevPack.Identity.Authentication.EntraId
Microsoft Entra ID connector: a third, opt-in authentication scheme that composes with the existing dual scheme (cookie BFF + self-issued Bearer) without replacing it. The DevPack ships the abstractions + the scheme-registration extension; the concrete wiring, the sign-in slice and the Graph synchronization live in the consumer (ADR-010).
Componentes
| Tipo | Nome | Responsabilidade |
|---|---|---|
| Interface | IEntraTenantValidator | Decide se um token Entra (tenant Entra do iss) é aceito para o tenant da aplicação |
| Record | EntraTenantValidationResult | Resultado de ValidateAsync — (IsValid, AppTenantId?, FailureReason?) + factories Success/Fail |
| Class | PerTenantSettingsEntraTenantValidator | Impl default: valida contra IPerTenantSettingsStore por EntraSettingKeys (enabled + diretório + audience tolerante) |
| Interface | IEntraClaimsMapper | Mapeia o ClaimsPrincipal Entra → dados para lookup/escopo do usuário local |
| Record | EntraUserInfo | (ObjectId, UserName?, Email?, Roles[]) extraído do principal |
| Class | DefaultEntraClaimsMapper | Impl default: lê oid/objectidentifier, name/preferred_username, roles/scp |
| Interface | IEntraGraphClientFactory | Interface-only: cria um Graph client por-tenant para JIT sync; impl concreta (com Microsoft.Graph) fica no consumidor |
| Static Class | EntraSettingKeys | Constantes das chaves per-tenant (AuthProvider.EntraID, EntraID.TenantId, EntraID.WebApi.ClientId, EntraID.Spa.*, ...) |
| Static Class | DevPackEntraIdServiceCollectionExtensions | AddDevPackEntraId(...) + ExtractTenantIdFromIssuer(...) |
Como usar
// Registra o scheme Entra ao lado do dual scheme existente (NÃO substitui o DefaultProvider).
builder.Services
.AddAuthentication(IdentitySchemas.Smart.Name)
.AddDevPackDualScheme(selector => /* cookie vs bearer próprio, como hoje */)
.AddCookie(...)
.AddJwtBearer(/* token próprio, scheme DefaultProvider */)
.AddDevPackEntraId(configure: idOptions =>
{
idOptions.Instance = "https://login.microsoftonline.com/";
idOptions.TenantId = "common"; // multi-tenant; diretório validado per-tenant no evento
idOptions.ClientId = "<web-api-client>"; // base; o aud per-tenant é validado pelo IEntraTenantValidator
});
// Validação per-tenant + claims mapper (a impl default do validator é registrada pelo consumidor).
builder.Services.AddScoped<IEntraTenantValidator, PerTenantSettingsEntraTenantValidator>();
// IEntraClaimsMapper já é registrado por AddDevPackEntraId (DefaultEntraClaimsMapper, via TryAdd).
O OnTokenValidated do scheme resolve IEntraTenantValidator do RequestServices, extrai o tenant Entra do
iss (ExtractTenantIdFromIssuer) e chama ValidateAsync; se inválido, context.Fail(reason). O lookup do
usuário local + emissão do token próprio do app ficam no slice de sign-in do consumidor (padrão "Entra
autentica, app emite token próprio").
Validação de issuer e audience
- Issuer: aceita
https://login.microsoftonline.com/{tid}/v2.0(v2) ehttps://sts.windows.net/{tid}/(v1). Hosts não-Microsoft são rejeitados. - Audience: o
PerTenantSettingsEntraTenantValidatorvalida oaudcontraEntraSettingKeys.WebApiClientIddo tenant — mais seguro que desligarValidateAudience. É tolerante: só exige quando o tenant tem o client id configurado e o token carrega umaud. Aceita{id}eapi://{id}.
Chaves per-tenant (EntraSettingKeys)
AuthProvider.EntraID (flag enabled, idêntica à chave canon do template) + a família EntraID.*
(EntraID.TenantId, EntraID.WebApi.ClientId, EntraID.WebApi.ClientSecret, EntraID.Spa.ClientId,
EntraID.Spa.RedirectUri, ...). O secret é escrito com mustProtect: true. O tenant da aplicação é resolvido
antes da autenticação (resolução por domínio → ICurrentUser.TenantId, DevPack ADR-007), pois o store é
indexado por tenant da aplicação e não há lookup reverso a partir do tenant Entra.
O que não fazer
- Não registrar um scheme por request — schemes são estáticos; a config per-tenant vai nos eventos (ADR-008).
- Não substituir o
DefaultProvider— Entra é o terceiro scheme, opt-in por tenant (ADR-010). - Não referenciar
Microsoft.Graphaqui —IEntraGraphClientFactoryé interface-only; a impl fica no consumidor. - Não desligar
ValidateAudiencecegamente — prefira a validação per-tenant tolerante do validator default. - Não logar/expor o
ClientSecret— é lido decifrado do store e nunca deve aparecer noGetPublicAuthConfig.
Extensão pelo produto
- Implementar
IEntraGraphClientFactory(comClientSecretCredential+GraphServiceClient, cacheado por tenant) para JIT sync de displayName/UPN. - Substituir
IEntraTenantValidator/IEntraClaimsMapperquando a regra de aceite ou o mapeamento de claims divergir do default. - O slice de sign-in (
SigninWithEntra), o endpoint e a persistência dasEntraSettingKeysvivem no consumidor.