Files
crm-calls/calls/backend/Calls.Api/JitsiToken.cs

62 lines
2.5 KiB
C#

using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace Calls.Api;
/// <summary>
/// Пропуск в Jitsi: HS256 JWT на одну комнату (prosody token auth, AUTH_TYPE=jwt).
/// Права модератора — context.user.moderator (модуль prosody token_affiliation).
/// </summary>
public sealed class JitsiToken
{
public string Domain { get; }
public string AppId { get; }
public string Audience { get; }
public TimeSpan Ttl { get; }
private readonly byte[] _secret;
public JitsiToken(IConfiguration cfg)
{
Domain = (cfg["Jitsi:Domain"] ?? "meet.volcanic.tes.gd").Trim();
AppId = (cfg["Jitsi:AppId"] ?? "tvorymo-crm").Trim();
Audience = (cfg["Jitsi:Audience"] ?? "jitsi").Trim();
Ttl = TimeSpan.FromMinutes(double.TryParse(cfg["Jitsi:TokenTtlMinutes"], out var m) && m > 0 ? m : 15);
var secret = cfg["Jitsi:AppSecret"] ?? "";
if (secret.Length < 32) throw new InvalidOperationException("Jitsi:AppSecret (JWT_APP_SECRET) не задан или короче 32 символов");
_secret = Encoding.UTF8.GetBytes(secret);
}
public sealed record Pass(string Jwt, long Exp);
public Pass Issue(string room, string userId, string name, string? email, bool moderator, DateTimeOffset now)
{
var exp = (now + Ttl).ToUnixTimeSeconds();
var user = new Dictionary<string, object?> { ["id"] = userId, ["name"] = name, ["moderator"] = moderator };
if (!string.IsNullOrEmpty(email)) user["email"] = email;
var payload = new Dictionary<string, object?>
{
["iss"] = AppId,
["aud"] = Audience,
["sub"] = Domain,
["room"] = room,
["iat"] = now.ToUnixTimeSeconds(),
["nbf"] = now.ToUnixTimeSeconds() - 30,
["exp"] = exp,
["context"] = new Dictionary<string, object?> { ["user"] = user },
["moderator"] = moderator,
};
return new Pass(Sign(payload), exp);
}
public string Sign(object payload)
{
var header = B64(JsonSerializer.SerializeToUtf8Bytes(new { alg = "HS256", typ = "JWT" }));
var body = B64(JsonSerializer.SerializeToUtf8Bytes(payload));
var sig = B64(HMACSHA256.HashData(_secret, Encoding.ASCII.GetBytes(header + "." + body)));
return header + "." + body + "." + sig;
}
public static string B64(byte[] b) => Convert.ToBase64String(b).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}