fengling-gateway/Middleware/JwtTransformMiddleware.cs

84 lines
2.6 KiB
C#

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using YarpGateway.Config;
namespace YarpGateway.Middleware;
public class JwtTransformMiddleware
{
private readonly RequestDelegate _next;
private readonly JwtConfig _jwtConfig;
private readonly ILogger<JwtTransformMiddleware> _logger;
public JwtTransformMiddleware(
RequestDelegate next,
IOptions<JwtConfig> jwtConfig,
ILogger<JwtTransformMiddleware> logger
)
{
_next = next;
_jwtConfig = jwtConfig.Value;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
{
await _next(context);
return;
}
var token = authHeader.Substring("Bearer ".Length).Trim();
try
{
var jwtHandler = new JwtSecurityTokenHandler();
var jwtToken = jwtHandler.ReadJwtToken(token);
var tenantId = jwtToken.Claims.FirstOrDefault(c => c.Type == "tenant")?.Value;
var userId = jwtToken
.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)
?.Value;
var userName = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
var roles = jwtToken
.Claims.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value)
.ToList();
if (!string.IsNullOrEmpty(tenantId))
{
context.Request.Headers["X-Tenant-Id"] = tenantId;
if (!string.IsNullOrEmpty(userId))
context.Request.Headers["X-User-Id"] = userId;
if (!string.IsNullOrEmpty(userName))
context.Request.Headers["X-User-Name"] = userName;
if (roles.Any())
context.Request.Headers["X-Roles"] = string.Join(",", roles);
_logger.LogInformation(
"JWT transformed - Tenant: {Tenant}, User: {User}",
tenantId,
userId
);
}
else
{
_logger.LogWarning("JWT missing tenant claim");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to parse JWT token");
}
await _next(context);
}
}