Telegram Mini App v1: initData auth bridge into the existing site

- POST /api/public/tg-webapp-auth: HMAC-verifies WebApp initData against the
  companion bot token (12h freshness, timing-safe), maps chat -> member via
  tg-links.json, mints a message session -> linked members land on /my/<id>
  with zero login
- /app entry page (vendored telegram-web-app.js keeps CSP script-src 'self');
  unlinked users get the one-time wallet-link instructions
- tg-app.js on all pages: no-op in browsers; inside the webview lazy-loads the
  SDK, expands, themes header/background #071421, wires native BackButton
- Bot menu button set programmatically to open /app; /start + help mention it
- Synced chat.js canned answer + AI system prompt (Mini App facts)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-23 05:56:51 -05:00
parent 2a666d4063
commit cd361a1eaa
19 changed files with 3567 additions and 19 deletions
+11 -1
View File
@@ -89,6 +89,16 @@ function verifyChallenge(address, signature) {
saveSessions();
return { token, id };
}
// Mini App sessions: identity was already proved once (wallet-verified
// Telegram link), and Telegram re-proves the chat via signed initData — so a
// session can be minted without a fresh wallet signature. No address attached.
function mintSession(id) {
if (!Number.isInteger(id) || id < 1) return null;
const token = crypto.randomBytes(32).toString('hex');
sessions.set(token, { address: null, id, expires: Date.now() + SESSION_TTL, via: 'tg' });
saveSessions();
return token;
}
function authFromCookie(req) {
const m = /(?:^|;\s*)ctb\.msid=([^;]+)/.exec(req.headers.cookie || '');
if (!m) return null;
@@ -168,4 +178,4 @@ function adminList() {
return getMessages().slice(-300).reverse().map(m => ({ mid: m.mid, fromId: m.fromId, toId: m.toId || null, org: !!m.org, body: m.body, ts: m.ts, readCount: Object.keys(m.read || {}).length }));
}
module.exports = { init, makeChallenge, verifyChallenge, authFromCookie, sessionCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };
module.exports = { init, makeChallenge, verifyChallenge, mintSession, authFromCookie, sessionCookie, send, inbox, markRead, unreadCount, adminList, ADDR_RE };