Skip to content

Instantly share code, notes, and snippets.

@sanbiv
Last active December 6, 2024 07:49
Show Gist options
  • Save sanbiv/f932f27210479b98c0751c7f2bba96a1 to your computer and use it in GitHub Desktop.
Save sanbiv/f932f27210479b98c0751c7f2bba96a1 to your computer and use it in GitHub Desktop.
Telegram WebApp: validate initData
package telegram
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
)
type TelegramInitData struct {
AuthDate int
QueryID string
User TelegramInitDataUser
}
type TelegramInitDataUser struct {
ID int
FirstName string
LastName string
Username string
LanguageCode string
}
func hmacSha256(data, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
// Write Data to it
h.Write([]byte(data))
return string(h.Sum(nil))
}
func sortedKeys(qs url.Values) []string {
result := make([]string, 0, len(qs))
for k := range qs {
result = append(result, k)
}
sort.Strings(result)
return result
}
func buildCheckString(qs url.Values) string {
keys := sortedKeys(qs)
values := make([]string, 0)
for _, k := range keys {
if k == "hash" {
continue
}
values = append(values, fmt.Sprintf("%s=%s", k, qs.Get(k)))
}
return strings.Join(values, "\n")
}
// ValidateTelegramInitData validates Web App input https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
func ValidateTelegramInitData(initDataString, botToken string) (*TelegramInitData, error) {
//initDataString := r.URL.Query().Get("tgInitData")
if initDataString == "" {
return nil, errors.New("initData is empty")
}
qs, err := url.ParseQuery(initDataString)
if err != nil {
return nil, errors.New("initData is not a valid query string")
}
hash := qs.Get("hash")
checkString := buildCheckString(qs)
// WebAppData is needed. See: https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
secretKey := hmacSha256(botToken, "WebAppData")
dataHash := hex.EncodeToString([]byte(hmacSha256(checkString, secretKey)))
//dataHash := hmacSha256(checkString, secretKey)
if dataHash == hash {
// data is from Telegram
authDate, _ := strconv.Atoi(qs.Get("auth_date"))
uid, _ := strconv.Atoi(qs.Get("user.id"))
userData := map[string]any{}
err := json.Unmarshal([]byte(qs.Get("user")), &userData)
if err != nil {
return nil, errors.New("initData.user is not a valid json")
}
initData := &TelegramInitData{
AuthDate: authDate,
QueryID: qs.Get("query_id"),
User: TelegramInitDataUser{
ID: uid,
FirstName: userData["first_name"].(string),
LastName: userData["last_name"].(string),
Username: userData["username"].(string),
LanguageCode: userData["language_code"].(string),
},
}
return initData, nil
}
return nil, errors.New("initData is invalid")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment