telega_webapp
Telegram Mini Apps (Web Apps) support for Telega.
A Mini App receives a signed initData string from the Telegram client.
Your backend must verify that string before trusting any of the user data
inside it. This module covers the full server-side flow:
validate/validate_with_max_age— verify theHMAC-SHA256signature produced with your bot token (the standard first-party check) and parse the payload into typed values.validate_third_party— verify theEd25519signaturefield, for apps opened on behalf of another bot.parse— decodeinitDataintoWebAppInitDatawithout checking any signature (use only on already-trusted input).answer_web_app_query— reply to an inline Mini App query with a result.
Validation
The signing scheme (see the official docs):
secret_key = HMAC_SHA256(bot_token, "WebAppData"), then the expected hash
is HMAC_SHA256(data_check_string, secret_key) where data_check_string
is every field except hash/signature, sorted by key and joined with
newlines as key=value.
import telega_webapp
// `init_data` is the raw query string from `Telegram.WebApp.initData`,
// forwarded by your frontend (e.g. in an `Authorization` header).
case telega_webapp.validate_with_max_age(token, init_data, 86_400) {
Ok(data) -> {
// Trusted. `data.user` is who opened the app.
todo
}
Error(_) -> todo // reject the request
}
Types
Which Telegram environment a third-party signature was issued by. Selects
the public key used for Ed25519 verification.
pub type Environment {
Production
Test
}
Constructors
-
Production -
Test
A chat the Mini App was launched from (attachment-menu apps only).
Mirrors the WebAppChat
object.
pub type WebAppChat {
WebAppChat(
id: Int,
type_: String,
title: String,
username: option.Option(String),
photo_url: option.Option(String),
)
}
Constructors
-
WebAppChat( id: Int, type_: String, title: String, username: option.Option(String), photo_url: option.Option(String), )
Reasons validation or parsing can fail.
pub type WebAppError {
MalformedInitData
InvalidField(String)
MissingHash
MissingSignature
SignatureMismatch
Outdated
}
Constructors
-
MalformedInitDataThe
initDatastring is not a valid URL-encoded query. -
InvalidField(String)A required field (named) was missing or could not be decoded.
-
MissingHashNo
hashfield — cannot run first-party validation. -
MissingSignatureNo
signaturefield — cannot run third-party validation. -
SignatureMismatchThe computed signature did not match the provided one.
-
Outdatedauth_dateis older than the allowedmax_age.
The decoded initData payload.
hash is the first-party HMAC-SHA256 signature and is empty when the app
was opened with only a third-party signature present.
pub type WebAppInitData {
WebAppInitData(
query_id: option.Option(String),
user: option.Option(WebAppUser),
receiver: option.Option(WebAppUser),
chat: option.Option(WebAppChat),
chat_type: option.Option(String),
chat_instance: option.Option(String),
start_param: option.Option(String),
can_send_after: option.Option(Int),
auth_date: Int,
hash: String,
signature: option.Option(String),
)
}
Constructors
-
WebAppInitData( query_id: option.Option(String), user: option.Option(WebAppUser), receiver: option.Option(WebAppUser), chat: option.Option(WebAppChat), chat_type: option.Option(String), chat_instance: option.Option(String), start_param: option.Option(String), can_send_after: option.Option(Int), auth_date: Int, hash: String, signature: option.Option(String), )
A Telegram user as described inside a Mini App initData payload.
Mirrors the WebAppUser
object — note this is not the same shape as the Bot API User.
pub type WebAppUser {
WebAppUser(
id: Int,
first_name: String,
last_name: option.Option(String),
username: option.Option(String),
language_code: option.Option(String),
is_bot: option.Option(Bool),
is_premium: option.Option(Bool),
added_to_attachment_menu: option.Option(Bool),
allows_write_to_pm: option.Option(Bool),
photo_url: option.Option(String),
)
}
Constructors
-
WebAppUser( id: Int, first_name: String, last_name: option.Option(String), username: option.Option(String), language_code: option.Option(String), is_bot: option.Option(Bool), is_premium: option.Option(Bool), added_to_attachment_menu: option.Option(Bool), allows_write_to_pm: option.Option(Bool), photo_url: option.Option(String), )
Values
pub fn answer_web_app_query(
client client: client.TelegramClient,
web_app_query_id web_app_query_id: String,
result result: types.InlineQueryResult,
) -> Result(types.SentWebAppMessage, error.TelegaError)
Reply to an inline Mini App query via answerWebAppQuery.
web_app_query_id comes from the web_app_data/WebAppData query sent by
the app; build result with telega/inline_mode or the raw
telega/model/types constructors.
pub fn is_fresh(
data data: WebAppInitData,
max_age_seconds max_age_seconds: Int,
now_unix now_unix: Int,
) -> Bool
Whether auth_date is within max_age_seconds of now_unix (both in Unix
seconds). Exposed for callers that supply their own clock.
pub fn parse(
init_data init_data: String,
) -> Result(WebAppInitData, WebAppError)
Decode init_data into typed values without verifying any signature.
Only use on input you have already validated (or trust for another reason);
for request handling use validate instead.
pub fn validate(
token token: String,
init_data init_data: String,
) -> Result(WebAppInitData, WebAppError)
Validate init_data against your bot token using the first-party
HMAC-SHA256 scheme and return the typed payload on success.
This does not check auth_date freshness — use
validate_with_max_age to also reject stale data,
which you almost always want in production.
pub fn validate_third_party(
bot_id bot_id: Int,
init_data init_data: String,
environment environment: Environment,
) -> Result(WebAppInitData, WebAppError)
Validate init_data issued for a third-party bot using the Ed25519
signature field. bot_id is the numeric id of the bot the Mini App was
opened for (the part before : in its token).
Use this when your service receives Mini App data for bots you don’t hold
the token of; otherwise prefer validate.
pub fn validate_with_max_age(
token token: String,
init_data init_data: String,
max_age_seconds max_age_seconds: Int,
) -> Result(WebAppInitData, WebAppError)
Like validate, but also rejects data whose auth_date is
older than max_age_seconds relative to the current system time.
A typical max_age is one day (86_400).