Pular para o conteúdo principal
Versão: main (dev) 🚧

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

TipoNomeResponsabilidade
InterfaceIEntraTenantValidatorDecide se um token Entra (tenant Entra do iss) é aceito para o tenant da aplicação
RecordEntraTenantValidationResultResultado de ValidateAsync(IsValid, AppTenantId?, FailureReason?) + factories Success/Fail
ClassPerTenantSettingsEntraTenantValidatorImpl default: valida contra IPerTenantSettingsStore por EntraSettingKeys (enabled + diretório + audience tolerante)
InterfaceIEntraClaimsMapperMapeia o ClaimsPrincipal Entra → dados para lookup/escopo do usuário local
RecordEntraUserInfo(ObjectId, UserName?, Email?, Roles[]) extraído do principal
ClassDefaultEntraClaimsMapperImpl default: lê oid/objectidentifier, name/preferred_username, roles/scp
InterfaceIEntraGraphClientFactoryInterface-only: cria um Graph client por-tenant para JIT sync; impl concreta (com Microsoft.Graph) fica no consumidor
Static ClassEntraSettingKeysConstantes das chaves per-tenant (AuthProvider.EntraID, EntraID.TenantId, EntraID.WebApi.ClientId, EntraID.Spa.*, ...)
Static ClassDevPackEntraIdServiceCollectionExtensionsAddDevPackEntraId(...) + 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) e https://sts.windows.net/{tid}/ (v1). Hosts não-Microsoft são rejeitados.
  • Audience: o PerTenantSettingsEntraTenantValidator valida o aud contra EntraSettingKeys.WebApiClientId do tenant — mais seguro que desligar ValidateAudience. É tolerante: só exige quando o tenant tem o client id configurado e o token carrega um aud. Aceita {id} e api://{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.Graph aqui — IEntraGraphClientFactory é interface-only; a impl fica no consumidor.
  • Não desligar ValidateAudience cegamente — prefira a validação per-tenant tolerante do validator default.
  • Não logar/expor o ClientSecret — é lido decifrado do store e nunca deve aparecer no GetPublicAuthConfig.

Extensão pelo produto

  • Implementar IEntraGraphClientFactory (com ClientSecretCredential + GraphServiceClient, cacheado por tenant) para JIT sync de displayName/UPN.
  • Substituir IEntraTenantValidator/IEntraClaimsMapper quando a regra de aceite ou o mapeamento de claims divergir do default.
  • O slice de sign-in (SigninWithEntra), o endpoint e a persistência das EntraSettingKeys vivem no consumidor.