50 lines
1.7 KiB
C#
50 lines
1.7 KiB
C#
|
|
using System.IdentityModel.Tokens.Jwt;
|
||
|
|
using System.Security.Claims;
|
||
|
|
using System.Text;
|
||
|
|
using Microsoft.IdentityModel.Tokens;
|
||
|
|
|
||
|
|
namespace w4c_workflows.Services;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 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.
|
||
|
|
/// </summary>
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|