Created
January 27, 2024 14:59
-
-
Save rheckart/d3bc1aea0ee2174c7f8e820b93bcb9f4 to your computer and use it in GitHub Desktop.
C# ASP.NET Middleware Code to Authenticate Telegram initData
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Extensions.Primitives; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Web; | |
using System.Text.Json; | |
using System.Net; | |
using Telegram.Bot.Types; | |
namespace AnimalTrackz.API.Middleware | |
{ | |
public class TelegramAuthMiddleware | |
{ | |
private readonly RequestDelegate _next; | |
private const string TokenKey = "WebAppData"; | |
private readonly byte[]? _secretHmac; | |
private readonly string _webHookSecretToken; | |
public TelegramAuthMiddleware(RequestDelegate next, string botToken, string webHookSecretToken) | |
{ | |
_next = next; | |
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(TokenKey)); | |
_secretHmac = hmac.ComputeHash(Encoding.UTF8.GetBytes(botToken)); | |
_webHookSecretToken = webHookSecretToken; | |
} | |
public async Task InvokeAsync(HttpContext context) | |
{ | |
// Webhook | |
if (context.Request.Headers.TryGetValue("X-Telegram-Bot-Api-Secret-Token", out var sentBotApiSecretToken)) | |
{ | |
if (sentBotApiSecretToken != _webHookSecretToken) | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.Unauthorized); | |
return; | |
} | |
} | |
else if (context.Request.Headers.TryGetValue("X-Telegram-Bot-InitData", out var initData)) // Website inside Telegram | |
{ | |
try | |
{ | |
if (StringValues.IsNullOrEmpty(initData)) | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.Unauthorized); | |
return; | |
} | |
var queryStringData = HttpUtility.ParseQueryString(initData.ToString()); | |
var dictData = new SortedDictionary<string, string>( | |
#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. | |
queryStringData.AllKeys.ToDictionary(key => key, key => queryStringData[key])! | |
#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. | |
); | |
var telegramAuthDateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(dictData["auth_date"])); | |
// auth_date too old check | |
if (DateTime.UtcNow.Subtract(telegramAuthDateTimeOffset.UtcDateTime).TotalMinutes > 60) | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.RequestTimeout); | |
return; | |
} | |
/*if (!ValidateTelegramHash(dictData)) | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.Unauthorized); | |
return; | |
}*/ | |
var telegramUser = JsonSerializer.Deserialize<User>(dictData["user"]); | |
context.Items.Add("TelegramUser", telegramUser); | |
context.Items.Add("TelegramChatInstance", dictData["chat_instance"]); | |
context.Items.Add("TelegramChatType", dictData["chat_type"]); | |
} | |
catch | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.Unauthorized); | |
return; | |
} | |
} | |
else | |
{ | |
context.Response.StatusCode = ((int)HttpStatusCode.Unauthorized); | |
return; | |
} | |
await _next(context); | |
} | |
private bool ValidateTelegramHash1(IDictionary<string, string> telegramFields) | |
{ | |
var hash = telegramFields["hash"]; | |
telegramFields.Remove("hash"); | |
var dataStringBuilder = new StringBuilder(); | |
foreach (var field in telegramFields.Where(field => !string.IsNullOrEmpty(field.Value))) | |
{ | |
dataStringBuilder.Append(field.Key); | |
dataStringBuilder.Append('='); | |
dataStringBuilder.Append(field.Value); | |
dataStringBuilder.Append('\n'); | |
} | |
dataStringBuilder.Remove(dataStringBuilder.Length - 1, 1); | |
using var hmacData = new HMACSHA256(_secretHmac!); | |
var signature = hmacData.ComputeHash(Encoding.UTF8.GetBytes(dataStringBuilder.ToString())); | |
var hmacDataHex = Convert.ToHexString(signature).ToLower(); | |
return hmacDataHex == hash; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment