diff --git a/apps/docs/packages/init-data-golang.md b/apps/docs/packages/init-data-golang.md index 2e8936afc..b163e35a0 100644 --- a/apps/docs/packages/init-data-golang.md +++ b/apps/docs/packages/init-data-golang.md @@ -1,8 +1,7 @@ # init-data-golang -The package provides utilities to work with the initialization data of Telegram Mini Apps. To learn -more about the initialization data and its usage, please refer to -the [documentation](../platform/launch-parameters.md). +The package that provides utilities to work with +the [initialization data](../platform/launch-parameters.md) of Telegram Mini Apps. ## Installation @@ -20,28 +19,68 @@ usage of old stolen initialization data. package main import ( - "fmt" - "time" + "fmt" + "time" - "github.com/telegram-mini-apps/init-data-golang" + initdata "github.com/telegram-mini-apps/init-data-golang" ) func main() { - // Init data in raw format. - initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" + // Init data in raw format. + initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" - // Telegram Bot secret key. - token := "7342037359:AAHI25ES9xCOMPokpYoz-p8XVrZUdygo2J4" + // Telegram Bot secret key. + token := "7342037359:AAHI25ES9xCOMPokpYoz-p8XVrZUdygo2J4" - // Define how long since init data generation date init data is valid. - expIn := 24 * time.Hour + // Define how long since init data generation date init data is valid. + expIn := 24 * time.Hour - // Will return error in case, init data is invalid. - fmt.Println(initdata.Validate(initData, token, expIn)) + // Will return error in case, init data is invalid. + fmt.Println(initdata.Validate(initData, token, expIn)) } ``` +### Third-Party Validation + +The package allows validating init data to check if it was signed by Telegram. + +To do so, call the `ValidateThirdParty` function with the following arguments: + +- `initData: string`: Init data in raw format. +- `botID: int64`: The Telegram Bot issuer identifier for the init data. +- `expIn: time.Time`: The maximum lifetime of the init data. + +Here is an example: + +```go +package main + +import ( + "fmt" + "time" + + initdata "github.com/telegram-mini-apps/init-data-golang" +) + +func main() { + // Init data in raw format. + initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733584787&hash=2174df5b000556d044f3f020384e879c8efcab55ddea2ced4eb752e93e7080d6&signature=zL-ucjNyREiHDE8aihFwpfR9aggP2xiAo3NSpfe-p7IbCisNlDKlo7Kb6G4D0Ao2mBrSgEk4maLSdv6MLIlADQ" + + // Telegram Bot secret key. + var botID int64 = 7342037359 + + // Define how long since init data generation date init data is valid. + expIn := 24 * time.Hour + + // Will return an error if init data is invalid. + fmt.Println(initdata.ValidateThirdParty(initData, botID, expIn)) +} +``` + +You can also use the `ValidateThirdPartyWithEnv` function with the additional boolean +argument responsible for marking the environment as the testing one. + ## Parsing It is important to note that the `Parse` function does not perform the same checks as the `Validate` @@ -52,21 +91,21 @@ the hash or expiration time. package main import ( - "fmt" - "time" + "fmt" - "github.com/telegram-mini-apps/init-data-golang" + initdata "github.com/telegram-mini-apps/init-data-golang" ) func main() { - // Init data in raw format. - initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" - - // Will return 2 values. - // 1. InitData isntance if passed data has a correct format. - // 2. Error in case, something is wrong. - fmt.Println(initdata.Parse(initData)) + // Init data in raw format. + initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" + + // Will return 2 values. + // 1. InitData instance if passed data has a correct format. + // 2. Error in case, something is wrong. + fmt.Println(initdata.Parse(initData)) } + ``` ## Signing @@ -79,39 +118,39 @@ itself. package main import ( - "fmt" - "time" + "fmt" + "time" - "github.com/telegram-mini-apps/init-data-golang" + initdata "github.com/telegram-mini-apps/init-data-golang" ) func main() { - // Init data in raw format. - initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" - - // Telegram Bot secret key. - token := "7342037359:AAHI25ES9xCOMPokpYoz-p8XVrZUdygo2J4" - - // Signing timestamp. - // Here we took the value from the initData variable - // above (auth_date query parameter). - authDate := time.Unix(1733509682, 0) - - // Signing query parameters. - // Returned values: - // 1. Parameters sign result ("hash" init data property). - // 2. Error occurring while parsing query string as query parameters. - fmt.Println(initdata.SignQueryString(initData, token, authDate)) - - // Signing the same query parameters presented as a map. - fmt.Println(initdata.Sign(map[string]string{ - "user": "{\"id\":279058397,\"first_name\":\"Vladislav + - ? \\/\",\"last_name\":\"Kibenko\",\"username\":\"vdkfrost\",\"language_code\":\"ru\",\"is_premium\":true,\"allows_write_to_pm\":true,\"photo_url\":\"https:\\/\\/t.me\\/i\\/userpic\\/320\\/4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg\"}", - "chat_instance": "8134722200314281151", - "chat_type": "private", - "signature": "TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg", - }, token, authDate)) - - // In the console, you should see the same results. + // Init data in raw format. + initData := "user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733509682&signature=TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg&hash=a433d8f9847bd6addcc563bff7cc82c89e97ea0d90c11fe5729cae6796a36d73" + + // Telegram Bot secret key. + token := "7342037359:AAHI25ES9xCOMPokpYoz-p8XVrZUdygo2J4" + + // Signing timestamp. + // Here we took the value from the initData variable + // above (auth_date query parameter). + authDate := time.Unix(1733509682, 0) + + // Signing query parameters. + // Returned values: + // 1. Parameters sign result ("hash" init data property). + // 2. Error occurring while parsing query string as query parameters. + fmt.Println(initdata.SignQueryString(initData, token, authDate)) + + // Signing the same query parameters presented as a map. + fmt.Println(initdata.Sign(map[string]string{ + "user": "{\"id\":279058397,\"first_name\":\"Vladislav + - ? \\/\",\"last_name\":\"Kibenko\",\"username\":\"vdkfrost\",\"language_code\":\"ru\",\"is_premium\":true,\"allows_write_to_pm\":true,\"photo_url\":\"https:\\/\\/t.me\\/i\\/userpic\\/320\\/4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg\"}", + "chat_instance": "8134722200314281151", + "chat_type": "private", + "signature": "TYJxVcisqbWjtodPepiJ6ghziUL94-KNpG8Pau-X7oNNLNBM72APCpi_RKiUlBvcqo5L-LAxIc3dnTzcZX_PDg", + }, token, authDate)) + + // In the console, you should see the same results. } ``` @@ -119,4 +158,4 @@ func main() { ## GoDoc To see GoDoc documentation, -visit [this link](https://pkg.go.dev/github.com/telegram-mini-apps/init-data-golang). +visit [this link](https://pkg.go.dev/github.com/telegram-mini-apps/init-data-golang). \ No newline at end of file diff --git a/apps/docs/platform/init-data.md b/apps/docs/platform/init-data.md index b185e66fa..132ec010f 100644 --- a/apps/docs/platform/init-data.md +++ b/apps/docs/platform/init-data.md @@ -1,3 +1,7 @@ +--- +outline: [ 2, 4 ] +--- + # Init Data In the list of [launch parameters](launch-parameters), initialization data is located in @@ -75,7 +79,19 @@ Init data validation is one of the most important parts in communication between client and server. It's validity guarantees, that init data can be trusted and used in the future code execution. -Knowing, that init data is presented as query parameters list, to validate +> [!TIP] +> To avoid possible problems related to the init data validation process, we recommend utilizing +> well-established and tested packages: +> - For Node: [@telegram-apps/init-data-node](../packages/telegram-apps-init-data-node) +> - For GoLang: [init-data-golang](../packages/init-data-golang.md) + +### Using Telegram Bot Token + +Init data validation is one of the most important parts in communication +between client and server. It's validity guarantees, that init data can be trusted +and used in the future code execution. + +Knowing that init data is presented as query parameters list, to validate them, developer should follow the steps: 1. Iterate over all key-value pairs and create an array of string values in @@ -84,90 +100,201 @@ them, developer should follow the steps: 2. Sort the computed array in the alphabetical order. 3. Create HMAC-SHA256 using key `WebAppData` and apply it to the Telegram Bot token, that is bound to your Mini App. -4. Create HMAC-SHA256 using the result of the 3-rd step as a key. Apply +4. Create HMAC-SHA256 using the result of the previous step as a key. Apply it to the pairs array joined with linebreak (`\n`) received in the 2-nd step and present the result as hex symbols sequence. 5. Compare the `hash` value received in the 1-st step with the result of the 4-th step. 6. If these values are equal, passed init data can be trusted. -::: tip -In real-world applications, it is recommended to use additional mechanisms for verifying -initialization data. For example, add their expiration date. This check can be implemented using -the `auth_date` parameter, which is responsible for the date when the parameters were created. This -solution will allow in case of theft of initialization data to prevent their constant use by an -attacker. -::: +#### Example -::: tip +##### 1. Initial Input -To avoid possible problems related to the init data validation process, we recommend utilizing -well-established and -tested packages: +- **Telegram Bot token**: This value is used to sign the init data, and we will need it in the later + steps of the validation process. -- For Node: [@telegram-apps/init-data-node](../packages/telegram-apps-init-data-node) -- For GoLang: [init-data-golang](../packages/init-data-golang.md) +``` +5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8 +``` -::: +- **Init data**: This data should be passed directly from the mini application to the server as-is. -### Example +``` +query_id=AAHdF6IQAAAAAN0XohDhrOrc&user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%7D&auth_date=1662771648&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2 +``` + +##### 2. Create Key-Value Pairs and Extract Signature -Let's imagine, we have this input: +As per the validation process, we should parse the init data as query parameters. Then, take all +key-value pairs excluding the `hash` key, join them with the `=` symbol, and sort them in +alphabetical order. The final array must be joined with the linebreak symbol (`\n`). + +The result will be: ``` -Telegram Bot token: -5768337691:AAGDAe6rjxu1cUgxK4BizYi--Utc3J9v5AU - -Init data: -user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22en%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%7D -&chat_instance=-3788475317572404878 -&chat_type=private -&auth_date=1709144340 -&hash=371697738012ebd26a111ace4aff23ee265596cd64026c8c3677956a85ca1827 +auth_date=1662771648\nquery_id=AAHdF6IQAAAAAN0XohDhrOrc\nuser={"id":279058397,"first_name":"Vladislav","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true} ``` -After the 1-st and 2-nd steps we should receive the following data: +The signature is the value in the `hash` key. In this case, it equals: -```js -// Sorted pairs. -[ - 'auth_date=1709144340', - 'chat_instance=-3788475317572404878', - 'chat_type=private', - 'user={"id":279058397,"first_name":"Vladislav","last_name":"Kibenko","username":"vdkfrost","language_code":"en","is_premium":true,"allows_write_to_pm":true}' -] +``` +c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2 +``` + +##### 3. Create Telegram Bot Token Signature + +To verify if the init data is signed correctly, we need to sign it ourselves. First, we must create +an HMAC-SHA256 signature of the Telegram Bot token, using the `WebAppData` value as the +cryptographic key. -// Hash. -'371697738012ebd26a111ace4aff23ee265596cd64026c8c3677956a85ca1827' +Here is the result: + +``` +HMAC-SHA256( + 5768337691:AAH5YkoiEuPk8-FZa32hStHTqXiLPtAEhx8, + WebAppData +) = a5c609aa52f63cb5e6d8ceb6e4138726ea82bbc36bb786d64482d445ea38ee5f ``` -Then, create HMAC-SHA256 required in the 3-rd step. It should be based on the -`WebAppData` string literal value and Telegram Bot token. +> [!WARNING] +> The received value must not be transformed into a hexadecimal sequence, as shown above. In the +> next step, use it as-is (an array of bytes), but you can use the hexadecimal value to check if +> your +> code generates the hash correctly. + +##### 4. Create and Compare Init Data Signature + +Finally, we compute the init data signature. + +To do this, create another HMAC-SHA256 signature using the transformed init data from Step 2 as the +input, and the hash we received in Step 3 as the cryptographic key. + +Here is the result: ``` HMAC-SHA256( - "WebAppData", - "5768337691:AAGDAe6rjxu1cUgxK4BizYi--Utc3J9v5AU" -) = "aa492a44bdf019c759defb1698c1d77690189973945491a756051cdc1207a449" + auth_date=1662771648\nquery_id=AAHdF6IQAAAAAN0XohDhrOrc\nuser={"id":279058397,"first_name":"Vladislav","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true}, + *signature from Step 3*, +) = c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2 +``` + +Now, by comparing the result with the `hash` value from Step 2, we can confirm that they are +identical. This means we can trust the passed init data. + +### Using Telegram Public Key + +Another useful feature Telegram provides is it allows validating init data without knowing +the bot secret token, but its identifier. + +At the moment, there are 2 Ed25519 public keys Telegram provides: + +- For production environment: `e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d` +- For test environment: `40055058a4ee38156a06562e52eece92a771bcd8346a8c4615cb7376eddf72ec` + +To perform this kind of validation (called third-party validation), follow these steps: + +1. Iterate over all key-value pairs and create an array of string values in + format `{key}={value}`. Key `hash` should be excluded. The `signature` key should also be + excluded, but memoized. It represents the init data sign and will be used in the final step of + the validation process. +2. Sort the computed array in the alphabetical order. +3. Concatenate Telegram Bot identifier issued the init data with the `WebAppData` string using the + semicolon (`:`) and append a linebreak (`\n`). +4. Join the pairs from the 2-nd step with the linebreak (`\n`) and append the final value to the + value from the 3-rd step. +5. Verify Ed25519 signature using the value from the `signature` init data key. +6. If verification is successful, the init data can be trusted. + +#### Example + +##### 1. Initial Input + +- **Production environment**: We are validating init data in the production environment. So, this + Ed25519 public key will be used: + +``` +e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d ``` -Finally, let's compute the init data sign using the sorted pairs received in -the 2-nd step and the value from the 3-rd step: +- **Init data**: This data should be passed directly from the mini application to the server as-is. ``` -joined_pairs = - "auth_date=1709144340 - chat_instance=-3788475317572404878 - chat_type=private - user={\"id\":279058397,\"first_name\":\"Vladislav\",\"last_name\":\"Kibenko\",\"username\":\"vdkfrost\",\"language_code\":\"en\",\"is_premium\":true,\"allows_write_to_pm\":true}" +user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%20%2B%20-%20%3F%20%5C%2F%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%2C%22allows_write_to_pm%22%3Atrue%2C%22photo_url%22%3A%22https%3A%5C%2F%5C%2Ft.me%5C%2Fi%5C%2Fuserpic%5C%2F320%5C%2F4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg%22%7D&chat_instance=8134722200314281151&chat_type=private&auth_date=1733584787&hash=2174df5b000556d044f3f020384e879c8efcab55ddea2ced4eb752e93e7080d6&signature=zL-ucjNyREiHDE8aihFwpfR9aggP2xiAo3NSpfe-p7IbCisNlDKlo7Kb6G4D0Ao2mBrSgEk4maLSdv6MLIlADQ +``` + +##### 2. Create Key-Value Pairs and Extract Signature + +As per the validation process, we should parse the init data as query parameters. Then, take all +key-value pairs excluding the `hash` and `signature` keys, join them with the `=` symbol, and sort +them in alphabetical order. The final array must be joined with the linebreak symbol (`\n`). + +The result will be: + +``` +auth_date=1733584787\nchat_instance=8134722200314281151\nchat_type=private\nuser={"id":279058397,"first_name":"Vladislav + - ? \/","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true,"allows_write_to_pm":true,"photo_url":"https:\/\/t.me\/i\/userpic\/320\/4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg"} +``` + +The signature is the value in the `signature` key. In this case, it equals: -HMAC-SHA256( - "aa492a44bdf019c759defb1698c1d77690189973945491a756051cdc1207a449", - joined_pairs, -) = "371697738012ebd26a111ace4aff23ee265596cd64026c8c3677956a85ca1827" ``` +zL-ucjNyREiHDE8aihFwpfR9aggP2xiAo3NSpfe-p7IbCisNlDKlo7Kb6G4D0Ao2mBrSgEk4maLSdv6MLIlADQ +``` + +Nevertheless, in the future steps, we should convert it to a bytes array, assuming that this value +is base64-encoded. + +> [!DANGER] +> At the moment, Telegram sends an invalid signature. Some programming languages (e.g., Go) consider +> the signature as an invalid base64 value, as long as it doesn't fully comply with the standard. +> When using the signature, remember to add paddings (`=` signs) at the end of the value in case +> they are needed. In this example, the proper value would be: +> ``` +> zL-ucjNyREiHDE8aihFwpfR9aggP2xiAo3NSpfe-p7IbCisNlDKlo7Kb6G4D0Ao2mBrSgEk4maLSdv6MLIlADQ== +> ``` + +##### 3. Create Data-Check String + +Now, let's create a data-check string. To do so, we should join the Telegram Bot token with +the `WebAppData` string using the semicolon (`:`) and append a linebreak. Then, the sorted init data +from the previous step should be appended. -Now, comparing the last received result with the `hash` value from the 1-st step, -we can see, that they are equal. It means, we can trust the passed init data. +Here is the result: + +``` +7342037359:WebAppData\nauth_date=1733584787\nchat_instance=8134722200314281151\nchat_type=private\nuser={"id":279058397,"first_name":"Vladislav + - ? \/","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true,"allows_write_to_pm":true,"photo_url":"https:\/\/t.me\/i\/userpic\/320\/4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg"} +``` + +##### 4. Verify Ed25519 Signature + +Finally, we verify the signature. + +As long as we are in the production environment, we will use the corresponding Ed25519 public key: + +``` +e7bf03a2fa4602af4580703d88dda5bb59f32ed8b02a56c187fe7d34caed242d +``` + +This value must be converted to a bytes array, assuming that the value is a hexadecimal sequence. + +So here is the result of verification: + +``` +Ed25519-Verify( + 7342037359:WebAppData\nauth_date=1733584787\nchat_instance=8134722200314281151\nchat_type=private\nuser={"id":279058397,"first_name":"Vladislav + - ? \/","last_name":"Kibenko","username":"vdkfrost","language_code":"ru","is_premium":true,"allows_write_to_pm":true,"photo_url":"https:\/\/t.me\/i\/userpic\/320\/4FPEE4tmP3ATHa57u6MqTDih13LTOiMoKoLDRG4PnSA.svg"}, + *public key (as a hexadecimal sequence) converted to bytes array*, + *signature (as a base64-encoded value) converted to bytes array* +) == true +``` + +If the verification is successful, the init data can be trusted. + +### Recommendation + +In real-world applications, it is recommended to use additional mechanisms for verifying +initialization data. For example, add their expiration date. This check can be implemented using +the `auth_date` parameter, which is responsible for the date when the parameters were created. +This solution will allow in case of theft of initialization data to prevent their constant use by +an attacker. ## Parameters List