Pular para o conteúdo principal
Versão: 10.4.1

ITLab.Template.DevPack.Identity

Defines identity context contracts, provider constants and user scope helpers.

Componentes

TipoNomeResponsabilidade
InterfaceICurrentUserRead and scope user context for current execution
InterfaceICurrentUserAccessorStore and retrieve ambient UserInfo
ClassCurrentUserDefault ICurrentUser implementation
ClassAsyncLocalCurrentUserAccessorAsync-flow storage for current user snapshot
ClassUserInfoUser context data model
EnumEIdentityProviderSupported identity provider kinds
ClassIdentitySchemasAuthentication scheme and policy constants
Abstract ClassBaseIdentityUserBase ASP.NET Core IdentityUser<int> with domain events, audit tracking, soft-delete, and activation
Abstract ClassBaseIdentityRoleBase ASP.NET Core IdentityRole<int> with domain events, audit tracking, activation, and multi-tenancy
Static ClassCurrentUserExtensionsExtension methods for ICurrentUser; provides AsSystemTenant for background operation scopes
Static ClassIdentityProviderNameCanonical string constants for identity provider names (DefaultProvider, MicrosoftEntraID, Ldap)

Como usar

// Typical registration for request-scoped user context.
services.AddSingleton<ICurrentUserAccessor>(AsyncLocalCurrentUserAccessor.Instance);
services.AddScoped<ICurrentUser, CurrentUser>();

// Temporary system scope for background operations.
using (currentUser.AsSystemTenant(tenantId: 42))
{
await service.ExecuteTenantMaintenanceAsync();
}

Authentication scheme — production checklist

ASP.NET Core exige um DefaultChallengeScheme configurado para o pipeline de autorização. Sem um, qualquer request não autenticado a um endpoint RequireAuthorization() levanta InvalidOperationException → 500 (em vez de 401). Configure um dos esquemas:

JWT Bearer (mais comum em APIs)

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
{
opt.Authority = builder.Configuration["Auth:Authority"];
opt.Audience = builder.Configuration["Auth:Audience"];
// ...
});

Microsoft Entra ID (anteriormente Azure AD)

Ver AuthSettings/README.md — DTOs tipados (EntraIdWebApiSettings, EntraIdSpaSettings, EntraIdAgentSettings) cobrem os três fluxos canônicos.

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(...);

Pipeline order

app.UseAuthentication();
app.UseDevPackIdentityContext(); // bridge HttpContext.User → AsyncLocal (DevPack 10.3.0+)
app.UseAuthorization();

Ordem crítica: UseAuthentication popula HttpContext.User; UseDevPackIdentityContext projeta as claims em AsyncLocalCurrentUserAccessor (consumido por RoleBasedPermissionEvaluator); UseAuthorization então avalia as policies. Esquecer o middleware central → RoleBasedPermissionEvaluator recebe UserRoles = [] → 403 em tudo, mesmo com auth correto.

Template scaffold note

O template ITLab.Template.Next nasce com autenticação funcional (ADR-006 do template): ASP.NET Identity local + dual-scheme — cookie BFF HttpOnly para browser e Bearer JWT assimétrico validado por JWKS para mobile/service-to-service — montados sobre os blocos desta área (BaseIdentityUser/Role, ITokenService, ISigningKeyProvider, JwksDocumentBuilder, AddDevPackDualScheme; ver § Self-issued tokens & dual scheme). Não existe mais placeholder de autenticação. Ver CLAUDE.md § Authentication do template.

Self-issued tokens & dual scheme (10.4.0)

Para apps que emitem os próprios tokens (Identity local), em vez de validar um IdP externo. Dois schemes coexistem: cookie HttpOnly para browser (BFF) e Bearer JWT assimétrico (validado por JWKS) para mobile/service-to-service. Ver ADR-005.

Componentes

TipoNomeResponsabilidade
InterfaceAuthentication.Bearer.ISigningKeyProviderChave de assinatura ativa + chaves públicas para JWKS
ClassEphemeralRsaSigningKeyProviderChave RSA em memória (dev)
ClassFileSigningKeyProviderChave RSA de PEM em disco (prod)
InterfaceAuthentication.Bearer.ITokenServiceEmite access token assinado a partir de claims
ClassDefaultJwtTokenServiceImplementação JWT RSA/ECDSA com kid
RecordAuthentication.Bearer.AccessTokenJWT assinado + instante de expiração
Static ClassAuthentication.Bearer.JwksDocumentBuilderConstrói o JWKS (RFC 7517) das chaves públicas
Abstract ClassRefreshTokenRefresh token persistido (hash, rotação, revogação)
InterfaceIRefreshTokenStorePersistência de refresh tokens (add/find/rotate/revoke)
Static ClassRefreshTokenGeneratorGera token opaco + hash de lookup
Abstract ClassServiceClientCliente de máquina (client-credentials) com secret BCrypt
OptionsAppSettings.IdentityTokenOptionsIssuer/Audience/lifetimes/PEM path (seção Authentication:Token)
Static ClassDevPackIdentityServiceCollectionExtensionsAddDevPackSigningKey / AddDevPackTokenService / AddDevPackDualScheme
Abstract ClassInfrastructure.EfCore.BaseIdentityDbContext<TUser,TRole>IdentityDbContext + convenções (schema identity, sem prefixo AspNet)

Como usar

// 1. Signing key: ephemeral em dev, PEM em prod (falha-cedo sem chave fora de dev).
builder.Services.AddDevPackSigningKey(
builder.Configuration[$"{IdentityTokenOptions.ConfigSectionPath}:SigningKeyPemPath"],
builder.Environment.IsDevelopment());

// 2. Token service (binda IdentityTokenOptions da seção "Authentication:Token").
builder.Services.AddDevPackTokenService(builder.Configuration);

// 3. Dual scheme: cookie (browser) + bearer (mobile/S2S), selecionado por request.
builder.Services
.AddAuthentication(IdentitySchemas.Smart.Name)
.AddDevPackDualScheme(ctx =>
ctx.Request.Headers.Authorization.ToString().StartsWith("Bearer ", StringComparison.Ordinal)
? JwtBearerDefaults.AuthenticationScheme
: CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o => { /* HttpOnly, Secure, SameSite */ })
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o => { /* ValidIssuer/Audience + IssuerSigningKeys públicas */ });

// 4. Publicar o JWKS para validadores externos (service-to-service).
app.MapGet("/.well-known/jwks.json", (ISigningKeyProvider keys) =>
Results.Json(JwksDocumentBuilder.Build(keys.PublicKeys)));

Tabelas de Identity — schema dedicado

BaseIdentityDbContext<TUser,TRole> move as sete tabelas de Identity para o schema identity (configurável via IdentitySchema) e dropa o prefixo AspNet: identity.Users, identity.Roles, identity.UserRoles, identity.UserClaims, identity.UserLogins, identity.UserTokens, identity.RoleClaims. Plugue os interceptors de audit/soft-delete via AddDevPackEfCoreInterceptors() — ver Infrastructure/EfCore/README.md.

O que não fazer (self-issued tokens)

  • Não usar chave simétrica quando há service-to-service — todo validador precisaria do segredo. Use assimétrica + JWKS.
  • Não publicar chave privada no JWKS — ISigningKeyProvider.PublicKeys expõe só material público e JwksDocumentBuilder nunca emite d/p/q.
  • Não persistir refresh token em claro — só o hash (RefreshTokenGenerator.Hash).
  • Não embrulhar AddCookie/AddJwtBearerAddDevPackDualScheme só pluga o seletor (ADR-001).

O que não fazer

  • Do not use AsSystemTenant to bypass user-facing authorization paths.
  • Do not persist UserInfo as long-lived session storage.

Extensão pelo produto

  • Products can derive from CurrentUser to customize IsAvailable and tenant resolution rules.
  • Products can add provider-specific auth settings under Identity/AuthSettings while preserving shared DTO contracts.