59 lines
2.0 KiB
C#
59 lines
2.0 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace GmRelay.Web.Services;
|
|
|
|
public sealed class TelegramAuthService(IConfiguration configuration)
|
|
{
|
|
public bool Verify(IQueryCollection query, out long telegramId, out string name)
|
|
{
|
|
telegramId = 0;
|
|
name = string.Empty;
|
|
|
|
if (!query.TryGetValue("hash", out var hash))
|
|
return false;
|
|
|
|
var token = configuration["Telegram__BotToken"] ?? configuration["Telegram:BotToken"];
|
|
if (string.IsNullOrEmpty(token))
|
|
return false;
|
|
|
|
// 1. Sort and join
|
|
var dataCheckList = query
|
|
.Where(x => x.Key != "hash")
|
|
.OrderBy(x => x.Key)
|
|
.Select(x => $"{x.Key}={x.Value}")
|
|
.ToList();
|
|
|
|
var dataCheckString = string.Join("\n", dataCheckList);
|
|
|
|
// 2. Compute Secret Key (static method — no IDisposable needed)
|
|
var secretKey = SHA256.HashData(Encoding.UTF8.GetBytes(token));
|
|
|
|
// 3. Compute Hash (static method — no IDisposable needed)
|
|
var computedHashBytes = HMACSHA256.HashData(secretKey, Encoding.UTF8.GetBytes(dataCheckString));
|
|
|
|
// 4. Timing-safe comparison to prevent timing attacks
|
|
var hashBytes = Convert.FromHexString(hash.ToString());
|
|
if (!CryptographicOperations.FixedTimeEquals(computedHashBytes, hashBytes))
|
|
return false;
|
|
|
|
// 5. Check expiration (auth_date)
|
|
if (query.TryGetValue("auth_date", out var authDateStr) && long.TryParse(authDateStr, out var authDate))
|
|
{
|
|
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
|
if (now - authDate > 86400) // 24 hours
|
|
return false;
|
|
}
|
|
|
|
if (query.TryGetValue("id", out var idStr) && long.TryParse(idStr, out telegramId))
|
|
{
|
|
var firstName = query["first_name"].ToString();
|
|
var lastName = query["last_name"].ToString();
|
|
name = string.IsNullOrWhiteSpace(lastName) ? firstName : $"{firstName} {lastName}";
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|