using System.Security.Claims; using line_gestao_api.Data; using Microsoft.EntityFrameworkCore; namespace line_gestao_api.Services; public class TenantMiddleware { private readonly RequestDelegate _next; public TenantMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync( HttpContext context, ITenantProvider tenantProvider, AppDbContext db) { Guid? tenantId = null; // Usuário autenticado: tenant vem do token. // Se token legado vier sem tenantId, tenta resolver pelo UserId no banco. if (context.User.Identity?.IsAuthenticated == true) { var claim = context.User.FindFirst("tenantId")?.Value ?? context.User.FindFirst("tenant")?.Value; if (Guid.TryParse(claim, out var parsedFromClaim) && parsedFromClaim != Guid.Empty) { tenantId = parsedFromClaim; } else { var userIdRaw = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? context.User.FindFirst("sub")?.Value; if (Guid.TryParse(userIdRaw, out var userId)) { var tenantFromUser = await db.Users .AsNoTracking() .IgnoreQueryFilters() .Where(u => u.Id == userId) .Select(u => (Guid?)u.TenantId) .FirstOrDefaultAsync(); if (tenantFromUser.HasValue && tenantFromUser.Value != Guid.Empty) { tenantId = tenantFromUser.Value; } } } // Evita comportamento silencioso de "dados vazios" quando token não traz tenant válido. if (!tenantId.HasValue) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; await context.Response.WriteAsync("Tenant inválido."); return; } } else { // Usuário anônimo: permite header para fluxos como login multi-tenant. var headerValue = context.Request.Headers["X-Tenant-Id"].FirstOrDefault(); if (Guid.TryParse(headerValue, out var headerTenant) && headerTenant != Guid.Empty) { tenantId = headerTenant; } } tenantProvider.SetTenantId(tenantId); try { await _next(context); } finally { tenantProvider.SetTenantId(null); } } }