Token Validation Mechanism
WebApps use a token validation mechanism to ensure the integrity and authenticity of the data passed to the WebApp. This is done through a hash-based signature validation process.
YoPhone provides an initData parameter, which contains signed user information. The WebApp must validate this data to prevent tampering or unauthorized access.
Steps for Token Validation#
- Extract initData
 When a WebApp is launched, it provides an initData string, which includes:- User ID, username, and other details
- Timestamp of the session
- Hash signature
 
Example initData:
auth_date=1234567890&hash=53a0fd101d226d24139793ebdca6cf0bfba3c062ab52c97eabf6ce163c65ca29&query_id=72d4e9cc-f80a-4822-b109-6db1046685eb&user={"first_name":"yo","id":"0192bcf9-4dda-7843-99a1-14535971bc14","language_code":"en","last_name":""}- Compute the Valid Hash
 To verify the integrity of the data:- Sort all parameters (except hash) in alphabetical order.
- Concatenate them into a string in key=valueformat.
- Generate an HMAC-SHA256 hash using the bot’s access token as the secret key.
 
- Sort all parameters (except 
Token Validation Examples#
Use one of the following language examples to validate initData.
- PHP
- Node.js
- Python
- Go
<?php   // The initialization data received (replace {initData} with actual data)   $initData = '{initData}';   $botAccessToken = 'botToken'; // Bot token used for authentication
   // Extract the hash value from the received data   $checkHash = $initData["hash"];   unset($initData["hash"]); // Remove the hash key to avoid including it in the verification process
   $keyValuePairs = [];
   foreach ($initData as $key => $value) {       // Combine each key-value pair into "key=value" format       $keyValuePairs[] = $key . "=" . $value;   }
   // Sort key-value pairs alphabetically by key   sort($keyValuePairs);
   // Create the data string to be hashed   $dataCheckString = implode("\n", $keyValuePairs);
   // Calculate the secret key using the bot token   $secretKey = hash_hmac('sha256', "WebAppData", $botAccessToken, true);
   // Generate the hash to compare with the received hash   $hash = hash_hmac('sha256', $dataCheckString, $secretKey);
   // Now, you can compare $hash with $checkHash to verify data integrity   if (hash_equals($hash, $checkHash)) {       echo "Data is valid!";   } else {       echo "Data is invalid!";   }?>const crypto = require("crypto");const querystring = require("querystring");
// The initialization data received (replace with actual data)const initData = "auth_date=1234567890&hash=53a0...&query_id=...";const botToken = "botToken";
// Parse initData into key-value pairsconst parsed = querystring.parse(initData);const checkHash = parsed.hash;delete parsed.hash;
// Create data string in sorted key=value\n formatconst dataCheckString = Object.keys(parsed)    .sort()    .map((key) => `${key}=${parsed[key]}`)    .join("\n");
// Compute secret key and hashconst secretKey = crypto    .createHmac("sha256", botToken)    .update("WebAppData")    .digest();
const hash = crypto    .createHmac("sha256", secretKey)    .update(dataCheckString)    .digest("hex");
// Compare hashesif (crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(checkHash))) {    console.log("Data is valid!");} else {    console.log("Data is invalid!");}import hmacimport hashlibfrom urllib.parse import parse_qsl
# The initialization data received (replace with actual data)init_data = 'auth_date=1234567890&hash=53a0...&query_id=...'bot_token = 'botToken'
# Parse initData into dictionarydata = dict(parse_qsl(init_data))check_hash = data.pop("hash", None)
# Create sorted key=value string separated by \ndata_check_string = "\n".join(    f"{k}={v}" for k, v in sorted(data.items()))
# Compute secret key and hashsecret_key = hmac.new(    key=bot_token.encode(), msg=b"WebAppData", digestmod=hashlib.sha256).digest()
valid_hash = hmac.new(    key=secret_key, msg=data_check_string.encode(), digestmod=hashlib.sha256).hexdigest()
# Compare hashesif hmac.compare_digest(valid_hash, check_hash):    print("Data is valid!")else:    print("Data is invalid!")package main
import (    "crypto/hmac"    "crypto/sha256"    "encoding/hex"    "fmt"    "net/url"    "sort"    "strings")
func main() {    initData := "auth_date=1234567890&hash=53a0...&query_id=..."    botToken := "botToken"
    values, _ := url.ParseQuery(initData)    checkHash := values.Get("hash")    values.Del("hash")
    // Build sorted key=value slice    keys := make([]string, 0, len(values))    for k := range values {        keys = append(keys, k)    }    sort.Strings(keys)
    var dataCheckString strings.Builder    for i, k := range keys {        if i > 0 {            dataCheckString.WriteString("\n")        }        dataCheckString.WriteString(k + "=" + values.Get(k))    }
    // Compute secret key and hash    secretKey := hmac.New(sha256.New, []byte(botToken))    secretKey.Write([]byte("WebAppData"))    key := secretKey.Sum(nil)
    hash := hmac.New(sha256.New, key)    hash.Write([]byte(dataCheckString.String()))    computedHash := hex.EncodeToString(hash.Sum(nil))
    // Compare hashes    if hmac.Equal([]byte(computedHash), []byte(checkHash)) {        fmt.Println("Data is valid!")    } else {        fmt.Println("Data is invalid!")    }}Security Considerations#
- Always validate initDataon the server-side to prevent spoofing.
- Use HTTPS to prevent MITM attacks.
- Ensure that auth_dateis recent to prevent replay attacks.