using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; namespace w4c_workflows.Services; /// /// Validates the main w4c JWT (signed with the shared Auth:JwtSigningKey) and /// extracts the tenant id. Used exclusively by the /api/keys* surface so a /// logged-in tenant can mint/rotate/revoke its own operator keys. /// public static class JwtValidator { static JwtValidator() { // Keep inbound claims exactly as issued ("tenant_id", "sub", ...). // JwtSecurityTokenHandler remaps claims to WS-Federation URIs by // default, which makes FindFirstValue("tenant_id") return null. JwtSecurityTokenHandler.DefaultMapInboundClaims = false; } public static ClaimsPrincipal? Validate(string? authHeader, string signingKey) { if (string.IsNullOrEmpty(signingKey) || string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) return null; try { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)); return new JwtSecurityTokenHandler().ValidateToken( authHeader["Bearer ".Length..].Trim(), new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = key, ValidateIssuer = false, ValidateAudience = false, ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(1), }, out _); } catch { return null; } } }