I recently had a task to make a few edits to the application and deploy it specifically as a Telegram Mini Application. Earlier, it was used exclusively in the browser.
Although the changes themselves weren’t difficult, the challenge emerged when it came to deploying the application in production conditions.
At first, I started with ChatGPT for guidance. However, I can say with certainty that the answers it provided were not accurate when considering the newer versions and updated interface interactions. Since I was focused on using the latest versions of the libraries, I decided to consult the official documentation instead.
Choosing the Right Framework Integration
After reading through and experimenting with several approaches extensively, I can confidently say that your initial step should be to choose an integration method based on the framework you’re using (React, Vue, Angular, etc.). Once that’s done, apply a solution tailored exclusively for your chosen framework. Doing so will simplify the entire process and save you time; otherwise, you might end up manually typing many structures and methods.
The project I worked on was built with React, and the best solution I found was to use @telegram-apps/sdk-react
Once installed, it can be initialized as follows:
import { init as initSDK } from '@telegram-apps/sdk-react';
initSDK();
Secure Backend Validation Workflow
Now, the most interesting part is how to securely integrate all of this with your backend. All user information, hashes, and related data are passed exclusively to the frontend, after which you must implement your own authorization and validation mechanisms for that information.
In other words, no additional data is sent in requests, cookies, or elsewhere.
Telegram’s own documentation says:
“To validate data received via the Mini App, you should send the data from the `Telegram WebApp.initData` field to the bot's backend. This data is a query string made up of a series of field-value pairs.”
Our approach is as follows: first, capture `InitData` on the frontend, send it unchanged to the backend, then use a hash function to compute the hash and compare it to the one received. The hash we calculate must match the one sent by the frontend, ensuring the request’s integrity.
A few important details about verification from official documentation:
“You can verify the integrity of received data by comparing the given hash parameter with the hexadecimal representation of the HMAC-SHA-256 signature of the data-check-string using the secret key, which itself is the HMAC-SHA-256 signature of the bot’s token combined with the constant string `WebAppData` used as a key.”
Understanding this validation logic, I implemented a `/authorization` endpoint on the backend. It performs simple JWT authorization—if the hash is valid, keys are issued. Afterward, the token is passed with every request as usual.
Conclusion
To sum everything up, our process looks like this:
- Retrieve `InitData` on the frontend
- Send the `InitData` unchanged to the backend
- Validate the received data and issue tokens
Everything seems clear at first, but as we know, there are many subtle details to keep in mind, especially regarding library versions and waiting properly for initialization on the frontend to ensure `InitData` is correctly obtained.