line-gestao-api/Services/TenantMiddleware.cs

85 lines
2.6 KiB
C#

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);
}
}
}