I’m working on authenticating users through the Telegram login widget but running into issues with hash verification. My approach involves extracting parameters from the callback URL, removing the hash value, sorting remaining parameters by key name, and creating a validation string with newline separators.
The process I follow:
- Extract the hash parameter and remove it from the collection.
- Sort the remaining parameters alphabetically.
- Create a verification string using key=value format.
- Generate HMAC-SHA256 using the bot token as a secret.
- Compare the computed hash with the received hash.
However, the hashes never match, and I can’t figure out what’s wrong.
I also have some questions about the implementation:
- Should URL-encoded values like photo_url be decoded first?
- Does the verification string need to be converted to lowercase?
Here’s my current code:
public IActionResult VerifyTelegramAuth([FromQuery] Dictionary<string, string> parameters)
{
if (!parameters.TryGetValue("hash", out var receivedHash) || string.IsNullOrEmpty(receivedHash))
{
return BadRequest("Hash parameter is missing");
}
parameters.Remove("hash");
if (parameters.ContainsKey("photo_url"))
{
parameters["photo_url"] = WebUtility.UrlDecode(parameters["photo_url"]);
}
var verificationString = string.Join("\n", parameters.OrderBy(pair => pair.Key)
.Select(pair => $"{pair.Key}={pair.Value}"));
var secretKey = ComputeSha256("MyBotToken");
var computedHash = ComputeHmacSha256(secretKey, Encoding.UTF8.GetBytes(verificationString));
var computedHashString = string.Concat(computedHash.Select(b => b.ToString("x2")));
if (computedHashString == receivedHash)
{
return Ok("Authentication successful");
}
else
{
return Unauthorized("Authentication failed");
}
}
private static byte[] ComputeSha256(string input)
{
using (SHA256 sha256 = SHA256.Create())
{
return sha256.ComputeHash(Encoding.UTF8.GetBytes(input));
}
}
private static byte[] ComputeHmacSha256(byte[] key, byte[] data)
{
using (var hmac = new HMACSHA256(key))
{
return hmac.ComputeHash(data);
}
}
Sample parameters I’m working with:
{
"user_id": "12345",
"name": "TestUser",
"login": "testuser123",
"avatar_url": "https%3A%2F%2Ft.me%2Favatar123.jpg",
"timestamp": "1640995200",
"hash": "abc123def456"
}