The Risk of Client-Side Trust
Integrating Telegram's login widget offers a streamlined authentication experience. Users can log into your web application using their existing Telegram accounts, bypassing the need to create new credentials. This frictionless flow is a significant win for user experience. However, blindly trusting the data sent back from the client-side widget—parameters like id, first_name, username, auth_date, and critically, the hash—opens a gaping security vulnerability.
Without proper server-side validation, an attacker could craft a malicious payload, spoofing a legitimate user's identity. This scenario, known as identity spoofing, can lead to unauthorized access and significant security breaches. The core problem lies in accepting these parameters directly without cryptographic proof of their origin and integrity. This guide focuses exclusively on the server-side validation in PHP using the Yii2 framework, assuming the frontend widget is already implemented.
Telegram's Verification Algorithm: HMAC-SHA-256
Telegram's authentication protocol relies on a secure method to verify the integrity and authenticity of the data submitted by the login widget. The process involves a specific algorithm that your backend must implement to trust the incoming user data.
Step 1: Sorting the Parameters
The first step in verifying the payload is to sort the incoming query parameters alphabetically by their keys. Exclude the hash parameter itself from this sorting process. This ensures a consistent order for the data that will be used in the signature calculation. For example, if your parameters are id=12345&first_name=Test&auth_date=1678886400&username=testuser, they would be sorted as:
auth_date=1678886400
id=12345
first_name=Test
username=testuser
Step 2: Constructing the Verification String
Next, construct a single string by concatenating the sorted key-value pairs, prefixed with their respective keys. Each key-value pair should be joined by an equals sign (=), and all pairs should be joined by a newline character (
). Using the sorted parameters from the previous step, the verification string would look like this:
auth_date=1678886400
id=12345
first_name=Test
username=testuser
Step 3: Calculating the HMAC-SHA-256 Signature
The crucial step involves calculating the HMAC-SHA-256 hash of the constructed verification string. This calculation requires your application's Bot Token, which acts as the secret key. You can obtain your Bot Token from BotFather on Telegram.
The HMAC-SHA-256 function takes two primary inputs: the message (the verification string) and the key (your Bot Token). The output is a hexadecimal hash representing the signature. This signature is what Telegram expects your application to generate based on the data it sent.
In PHP, this is typically done using the hash_hmac() function:
$botToken = 'YOUR_BOT_TOKEN';
$verificationString = "auth_date=1678886400\nid=12345\nfirst_name=Test\nusername=testuser";
$calculatedHash = hash_hmac('sha256', $verificationString, $botToken);
Step 4: Verifying the Hash
Compare the hash parameter received from the client with the $calculatedHash you just generated. If they match exactly, it means the data has not been tampered with and originated from Telegram. If they do not match, reject the authentication attempt immediately.
It's vital to perform this comparison in a cryptographically secure manner to prevent timing attacks. PHP's hash_equals() function is designed for this purpose:
$receivedHash = $_GET['hash']; // Assuming GET parameters
if (hash_equals($calculatedHash, $receivedHash)) {
// Hash is valid, proceed with further checks
} else {
// Hash mismatch, authentication failed
Yii::$app->response->statusCode = 401;
return ['error' => 'Invalid hash.'];
}
Enforcing Timestamp Expiration
Beyond cryptographic verification, it's essential to ensure the authentication attempt is recent. Telegram provides an auth_date parameter, which is a Unix timestamp indicating when the authentication occurred. This timestamp is also part of the data signed by the HMAC. If an attacker were to capture a valid payload and attempt to reuse it later, the auth_date would be old, but the hash would still be valid if they didn't re-sign it with their own token (which they can't).
Your backend should check that the auth_date is within an acceptable time window. A common practice is to set a maximum age for the authentication, such as 5 minutes (300 seconds). This prevents replay attacks where an old, valid login request is intercepted and submitted again later.
The check would look something like this:
$authDate = (int)$_GET['auth_date'];
$currentTime = time();
$maxAge = 300; // 5 minutes in seconds
if (($currentTime - $authDate) > $maxAge) {
// Auth date is too old, reject the request
Yii::$app->response->statusCode = 400;
return ['error' => 'Authentication request is too old.'];
}
Mapping to Yii2 User Records
Once the payload is securely verified (both the hash and the timestamp), you can proceed to map the Telegram user data to your application's user system within Yii2. The verified parameters, particularly the id (Telegram User ID), first_name, and username, are valuable for creating or logging in a user in your database.
Finding or Creating a User
In your Yii2 controller or a dedicated service, you would typically:
- Retrieve the verified Telegram User ID (
id). - Query your
Usermodel (or equivalent) to see if a user with this Telegram ID already exists. This might be stored in a dedicated column liketelegram_id. - If a user is found, log them in using Yii2's session management (e.g.,
Yii::$app->user->login()). - If no user is found, create a new user record, populating fields like
username,email(if available and requested), and crucially, thetelegram_id. After creating the user, log them in.
It's important to note that Telegram's widget can request specific user information. Ensure your application requests only the necessary fields and handles them appropriately. The id is the most critical piece of data for user identification and linking.

Security Best Practices Recap
To summarize the secure authentication flow:
- Never trust client-side data: Always validate on the server.
- Use HMAC-SHA-256: Verify the integrity of the payload using your Bot Token.
- Check the
auth_date: Ensure the authentication is recent to prevent replay attacks. - Use
hash_equals(): Perform hash comparisons securely. - Map to existing users: Link verified Telegram IDs to your application's user base.
By implementing these steps, you can leverage the convenience of the Telegram Login Widget while maintaining a robust security posture against identity spoofing and other related threats.
