validate.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package initdata
  2. import (
  3. "net/url"
  4. "sort"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. // Validate validates passed init data. This method expects initData to be
  10. // passed in the exact raw format as it could be found
  11. // in window.Telegram.WebApp.initData. Returns true in case init data is
  12. // signed correctly, and it is allowed to trust it.
  13. //
  14. // Current code is implementation of algorithmic code described in official
  15. // docs:
  16. // https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
  17. //
  18. // initData - init data passed from application;
  19. // token - TWA bot secret token which was used to create init data;
  20. // expIn - maximum init data lifetime. It is strongly recommended to use this
  21. // parameter. In case, exp duration is less than or equal to 0, function does
  22. // not check if parameters are expired.
  23. func Validate(initData, token string, expIn time.Duration) error {
  24. // Parse passed init data as query string.
  25. q, err := url.ParseQuery(initData)
  26. if err != nil {
  27. return ErrUnexpectedFormat
  28. }
  29. var (
  30. // Init data creation time.
  31. authDate time.Time
  32. // Init data sign.
  33. hash string
  34. // All found key-value pairs.
  35. pairs = make([]string, 0, len(q))
  36. )
  37. // Iterate over all key-value pairs of parsed parameters.
  38. for k, v := range q {
  39. // Store found sign.
  40. if k == "hash" {
  41. hash = v[0]
  42. continue
  43. }
  44. if k == "auth_date" {
  45. if i, err := strconv.Atoi(v[0]); err == nil {
  46. authDate = time.Unix(int64(i), 0)
  47. }
  48. }
  49. // Append new pair.
  50. pairs = append(pairs, k+"="+v[0])
  51. }
  52. // Sign is always required.
  53. if hash == "" {
  54. return ErrSignMissing
  55. }
  56. // In case, expiration time is passed, we do additional parameters check.
  57. if expIn > 0 {
  58. // In case, auth date is zero, it means, we can not check if parameters
  59. // are expired.
  60. if authDate.IsZero() {
  61. return ErrAuthDateMissing
  62. }
  63. // Check if init data is expired.
  64. if authDate.Add(expIn).Before(time.Now()) {
  65. return ErrExpired
  66. }
  67. }
  68. // According to docs, we sort all the pairs in alphabetical order.
  69. sort.Strings(pairs)
  70. // In case, our sign is not equal to found one, we should throw an error.
  71. if sign(strings.Join(pairs, "\n"), token) != hash {
  72. return ErrSignInvalid
  73. }
  74. return nil
  75. }