package auth_test import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "net/url" "sort" "strconv" "strings" "testing" "time" "scrabble/gateway/internal/auth" ) // signedInitData builds a valid Telegram initData query string for botToken, // computing the hash exactly as Telegram does. func signedInitData(botToken string, fields map[string]string) string { keys := make([]string, 0, len(fields)) for k := range fields { keys = append(keys, k) } sort.Strings(keys) lines := make([]string, 0, len(keys)) for _, k := range keys { lines = append(lines, k+"="+fields[k]) } secretMAC := hmac.New(sha256.New, []byte("WebAppData")) secretMAC.Write([]byte(botToken)) secret := secretMAC.Sum(nil) mac := hmac.New(sha256.New, secret) mac.Write([]byte(strings.Join(lines, "\n"))) hash := hex.EncodeToString(mac.Sum(nil)) v := url.Values{} for k, val := range fields { v.Set(k, val) } v.Set("hash", hash) return v.Encode() } func TestValidateAcceptsGenuineInitData(t *testing.T) { const token = "test-bot-token" fields := map[string]string{ "auth_date": strconv.FormatInt(time.Now().Unix(), 10), "query_id": "abc", "user": `{"id":42,"first_name":"Ann","username":"ann"}`, } u, err := auth.NewHMACValidator(token).Validate(signedInitData(token, fields)) if err != nil { t.Fatalf("validate genuine: %v", err) } if u.ID != "42" || u.Username != "ann" { t.Fatalf("user = %+v", u) } } func TestValidateRejectsTamperedHash(t *testing.T) { const token = "test-bot-token" fields := map[string]string{ "auth_date": strconv.FormatInt(time.Now().Unix(), 10), "user": `{"id":42}`, } data := signedInitData(token, fields) + "0" // corrupt the trailing hash if _, err := auth.NewHMACValidator(token).Validate(data); err == nil { t.Fatal("expected rejection of tampered init data") } } func TestValidateRejectsWrongToken(t *testing.T) { fields := map[string]string{ "auth_date": strconv.FormatInt(time.Now().Unix(), 10), "user": `{"id":42}`, } data := signedInitData("real-token", fields) if _, err := auth.NewHMACValidator("other-token").Validate(data); err == nil { t.Fatal("expected rejection under a different bot token") } } func TestValidateRejectsStaleInitData(t *testing.T) { const token = "test-bot-token" fields := map[string]string{ "auth_date": strconv.FormatInt(time.Now().Add(-48*time.Hour).Unix(), 10), "user": `{"id":42}`, } if _, err := auth.NewHMACValidator(token).Validate(signedInitData(token, fields)); err == nil { t.Fatal("expected rejection of stale init data") } }