# Telegram Mini App, Authentication, and Account Linking

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.me` start 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](../../../TELEGRAM_WALKTHROUGH/surface.md) 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:

1. Parse the query data without accepting duplicate/ambiguous security fields.
2. Remove `hash`, construct the sorted data-check string, and derive the secret
   from the bot token using Telegram's required process.
3. Compute and constant-time compare the HMAC.
4. Validate `auth_date` freshness with a configured maximum age and allowed
   clock skew.
5. Parse and validate Telegram user/chat/query identifiers and required fields.
6. 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

```mermaid
flowchart LR
  A[Authenticated Oshun member] --> B[Start Telegram link]
  B --> C[Verify Widget/update/deep-link proof]
  C --> D[Enforce uniqueness + confirmation]
  D --> E[Durable binding]
  E --> F[Bot/Mini App resolves member]
  E --> G[Unlink or revoke]
  G --> H[Stop delivery + invalidate sessions]
```

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](../../../WALKTHROUGH/customer/09-account/profile-telegram.md).

## 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.ts`
- `apps/oshun/bff/src/telegram/security-state.ts`
- `apps/oshun/bff/src/telegram/miniapp-surface.ts`
- `apps/oshun/telegram-miniapp/src/lib/telegram-webapp.ts`
- `apps/oshun/telegram-miniapp/src/lib/miniapp-client.ts`
- `apps/oshun/web/src/auth/telegram` and
  `apps/oshun/web/src/app/profile/telegram`
- `apps/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.
