feat: add telegram mini app dashboard
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace GmRelay.Web.Services;
|
||||
|
||||
@@ -55,4 +57,98 @@ public sealed class TelegramAuthService(IConfiguration configuration)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool VerifyWebAppInitData(string initData, out long telegramId, out string name)
|
||||
{
|
||||
telegramId = 0;
|
||||
name = string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(initData))
|
||||
return false;
|
||||
|
||||
var token = configuration["Telegram__BotToken"] ?? configuration["Telegram:BotToken"];
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return false;
|
||||
|
||||
var values = QueryHelpers.ParseQuery(initData);
|
||||
if (!values.TryGetValue("hash", out var hash) || string.IsNullOrWhiteSpace(hash))
|
||||
return false;
|
||||
|
||||
var dataCheckString = string.Join(
|
||||
"\n",
|
||||
values
|
||||
.Where(pair => pair.Key != "hash")
|
||||
.OrderBy(pair => pair.Key, StringComparer.Ordinal)
|
||||
.Select(pair => $"{pair.Key}={pair.Value}"));
|
||||
|
||||
var secretKey = HMACSHA256.HashData(Encoding.UTF8.GetBytes("WebAppData"), Encoding.UTF8.GetBytes(token));
|
||||
var computedHashBytes = HMACSHA256.HashData(secretKey, Encoding.UTF8.GetBytes(dataCheckString));
|
||||
|
||||
byte[] hashBytes;
|
||||
try
|
||||
{
|
||||
hashBytes = Convert.FromHexString(hash.ToString());
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CryptographicOperations.FixedTimeEquals(computedHashBytes, hashBytes))
|
||||
return false;
|
||||
|
||||
if (!values.TryGetValue("auth_date", out var authDateStr) ||
|
||||
!long.TryParse(authDateStr, out var authDate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
if (now - authDate > 86400)
|
||||
return false;
|
||||
|
||||
if (!values.TryGetValue("user", out var userJson) || string.IsNullOrWhiteSpace(userJson))
|
||||
return false;
|
||||
|
||||
return TryReadWebAppUser(userJson.ToString(), out telegramId, out name);
|
||||
}
|
||||
|
||||
private static bool TryReadWebAppUser(string userJson, out long telegramId, out string name)
|
||||
{
|
||||
telegramId = 0;
|
||||
name = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
using var document = JsonDocument.Parse(userJson);
|
||||
var root = document.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("id", out var idElement) || !idElement.TryGetInt64(out telegramId))
|
||||
return false;
|
||||
|
||||
var firstName = root.TryGetProperty("first_name", out var firstNameElement)
|
||||
? firstNameElement.GetString() ?? string.Empty
|
||||
: string.Empty;
|
||||
var lastName = root.TryGetProperty("last_name", out var lastNameElement)
|
||||
? lastNameElement.GetString() ?? string.Empty
|
||||
: string.Empty;
|
||||
var username = root.TryGetProperty("username", out var usernameElement)
|
||||
? usernameElement.GetString()
|
||||
: null;
|
||||
|
||||
name = (firstName, lastName) switch
|
||||
{
|
||||
({ Length: > 0 }, { Length: > 0 }) => $"{firstName} {lastName}",
|
||||
({ Length: > 0 }, _) => firstName,
|
||||
_ when !string.IsNullOrWhiteSpace(username) => "@" + username,
|
||||
_ => $"Telegram {telegramId.ToString(System.Globalization.CultureInfo.InvariantCulture)}"
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user