Telegram provides two related identity mechanisms that must not be confused: the
Login Widget can link/authenticate a web profile, while Mini App initData
proves that Telegram launched a particular embedded session. Both require
server-side signature and freshness verification; neither is a blanket grant to
every Oshun API.
Surface entry points#
- The bot menu button opens the configured Mini App base URL in a private chat.
- Inline Web App buttons can open a known Mini App surface where Telegram allows them.
t.mestart links provide a chat fallback for mapped surfaces and can enter the bot at a particular room.- The standalone Mini App landing lists curated surfaces;
/<surface>opens a surface detail. - The Oshun web profile page owns Telegram account link/status/unlink.
- Telegram Login Widget UI can initiate a verified web authentication/linking callback.
Curated Mini App surfaces#
The Mini App surface registry currently names seven curated views:
| Slug | Experience |
|---|---|
today |
Tara ritual and breath cues. |
sophia |
Grounded Q&A/evidence presentation. |
veritas |
Claim, sources/counterclaims, and regret/review affordance. |
nyx |
Sky window/layers and calendar affordance. |
arete |
Habit and mood check-in with humane recovery state. |
nisaba |
Passage/edition/lexicon reader and annotation affordance. |
illustration |
Governed illustration request/lineage presentation. |
Unknown slugs have a declared fallback behavior. The per-surface data and view
renderers live under apps/oshun/telegram-miniapp/src/app; browser coverage
lives under its e2e directory.
Honesty boundary for Mini App actions#
The Mini App walkthrough records which interactions are local state, which reach the BFF, and which are gaps. A button changing a status line to “queued,” “saved,” “added,” or “complete” is not a durable effect unless a verified session calls the real API and handles its acknowledgement/error. Product docs and tests must preserve that distinction as wiring evolves.
Each action should have one of these explicit states:
- local preview/demo interaction;
- authenticated read;
- durable write acknowledged by the system of record;
- accepted asynchronous job with job/result identity;
- unavailable because the adapter/scope is not connected;
- denied/step-up required;
- failed/partial with recovery guidance.
Telegram WebApp bridge and theme#
The client bridge detects Telegram's WebApp runtime, calls readiness/expansion only when available, reads color/theme parameters defensively, reacts to theme changes, and exposes safe close/back/main-button/haptic functions where used. Standalone browser rendering remains possible for development without pretending a Telegram identity exists.
Telegram theme values are untrusted presentation input. They map through a bounded semantic theme with contrast/focus fallbacks; raw colors do not erase danger, evidence, selected, disabled, or focus semantics. Light and dark Telegram themes, reduced motion, keyboard/reflow, and mobile viewport behavior are automated.
Mini App initData verification#
The server verifies the exact Telegram initData algorithm:
- Parse the query data without accepting duplicate/ambiguous security fields.
- Remove
hash, construct the sorted data-check string, and derive the secret from the bot token using Telegram's required process. - Compute and constant-time compare the HMAC.
- Validate
auth_datefreshness with a configured maximum age and allowed clock skew. - Parse and validate Telegram user/chat/query identifiers and required fields.
- Reject malformed, missing, stale, future, or mismatched data.
Verification happens in the BFF, not only in browser code. The raw bot token and
derived secret never enter the Mini App. Raw initData is sensitive and is not
logged, placed in analytics, or stored as durable member content.
Scoped Mini App session#
After successful verification, the BFF issues a short-lived session carrying the bound Telegram/user identity, allowed scopes, issue/expiry, audience/ issuer, and a unique/session identity suitable for revocation or replay controls. Allowed scopes are a closed vocabulary; the client cannot request an arbitrary API role.
Protected BFF requests validate signature, expiry, issuer/audience, scope, binding/account state, and resource/tenant authorization. Sensitive actions such as account/security/billing changes can require a primary Oshun credential challenge even when the Telegram session is valid.
CORS is allowlisted to the deployed Mini App origin. CORS is a browser boundary, not authentication; a non-browser caller still needs the verified session.
Login Widget verification#
Telegram Login Widget payload verification independently validates its field
set, bot-token-derived hash, timestamp freshness, and stable Telegram identity.
The verified payload can participate in Oshun authentication or account linking
only through the product's explicit flow. A browser-posted id or username
without verification is never trusted.
Display username/photo/name fields are profile metadata, not stable account keys. The numeric Telegram user id is the provider identity, and changes to display metadata do not create another binding.
Account binding lifecycle#
The binding store enforces that a Telegram identity cannot silently attach to multiple members and a member's binding changes are attributable/idempotent. Link flow state is short-lived, single-use, bound to the initiating member/ browser where applicable, and safe against guessing/replay.
Unlink can begin from the web profile or supported bot control, but it must use the durable adapter. It invalidates affected Mini App sessions and delivery associations and preserves a minimal audit record. The user receives an honest result when unlink is unavailable or fails.
The customer profile behavior is documented in Profile · Telegram.
Security and privacy rules#
- Do not put bot tokens, webhook secrets, raw initData, session tokens, binding nonces, or payment data into URLs/logs/analytics beyond protocol necessity.
- Treat Telegram usernames and profile photos as optional, changeable personal data; do not use them for authorization.
- Verify freshness and signature server-side every time an initial Telegram proof is exchanged.
- Bind Mini App sessions to least privilege and short lifetime; require primary credentials for high-impact changes.
- Re-authorize the target Oshun resource/tenant after Telegram identity verification.
- Support access/export/deletion and retention for binding and audit data.
- Rate-limit and deduplicate verification/link attempts without leaking whether another member owns the Telegram identity.
Failure and recovery#
| Failure | User/system behavior |
|---|---|
| Mini App opened outside Telegram | Render safe standalone/development shell; protected calls remain unavailable without verified proof. |
| Missing/invalid/stale initData | Do not issue session; explain that the app must be reopened from Telegram. |
| Invalid Login Widget payload | Reject authentication/linking without revealing token/hash details. |
| Binding collision | Withhold account details; require an authenticated recovery/support path. |
| Expired/revoked session | Clear protected state and re-run Telegram verification; do not retry writes with an invalid token. |
| Missing BFF adapter/scope | Display unavailable, not saved/success. |
| Unlink failure | Keep current binding/delivery state visible; allow bounded retry/support escalation. |
Source and verification#
libs/oshun/messaging-channels/src/telegram/security.tsapps/oshun/bff/src/telegram/security-state.tsapps/oshun/bff/src/telegram/miniapp-surface.tsapps/oshun/telegram-miniapp/src/lib/telegram-webapp.tsapps/oshun/telegram-miniapp/src/lib/miniapp-client.tsapps/oshun/web/src/auth/telegramandapps/oshun/web/src/app/profile/telegramapps/oshun/bff/src/messaging-channels/telegram-bindings-store.ts
Run signature/freshness/scope/session tests, durable binding integration, Mini App CORS/surface tests, web link/unlink browser journeys, Mini App browser journeys, theme/accessibility checks, and cross-account negative cases.