namespace MinecraftDiscordBot; public class TimeoutTokenProvider : ITokenProvider { public TimeoutTokenProvider(int instanceId, int timeoutSeconds, ICipher? cipher = null) { InstancePrefix = Convert.ToHexString(BitConverter.GetBytes(instanceId)); _timeout = timeoutSeconds; _cipher = cipher ?? new AesCipher(); } private readonly ICipher _cipher; private readonly int _timeout; public string InstancePrefix { get; } public bool VerifyToken(string token) { if (!token.StartsWith(InstancePrefix)) return false; token = token[InstancePrefix.Length..]; byte[] data; try { data = _cipher.Decrypt(Convert.FromHexString(token)); } catch (Exception e) { Program.LogError("TokenProvider", e); return false; } var when = DateTime.FromBinary(BitConverter.ToInt64(data, 0)); return when >= DateTime.UtcNow.AddSeconds(-_timeout); } public string GenerateToken() { var time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary()); var key = Guid.NewGuid().ToByteArray(); var token = InstancePrefix + Convert.ToHexString(_cipher.Encrypt(time.Concat(key).ToArray())); return token; } } public interface ITokenProvider { string GenerateToken(); bool VerifyToken(string token); }