From ee6ec5959c2e25deecfdbf93b79c04cb150dc7f2 Mon Sep 17 00:00:00 2001 From: Fringg Date: Mon, 23 Feb 2026 17:53:47 +0300 Subject: [PATCH] fix: detect Telegram account switch across tab closes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old clearStaleSessionIfNeeded stored initData in sessionStorage for comparison, but sessionStorage is cleared on tab close — so switching Telegram accounts between separate Mini App openings was never detected. Fix: store the Telegram user ID in localStorage (survives tab close). On every launch, compare the fresh initData user ID with the stored one. If they differ, clear all auth tokens and cached user data to force re-authentication as the correct account. --- src/utils/token.ts | 57 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/utils/token.ts b/src/utils/token.ts index add4724..60bd841 100644 --- a/src/utils/token.ts +++ b/src/utils/token.ts @@ -158,24 +158,73 @@ export const tokenStorage = { }, }; +/** + * Extract Telegram user ID from raw initData string. + * Does NOT validate cryptographic signature — used only for client-side identity comparison. + */ +function extractTelegramUserId(initData: string): string | null { + try { + const params = new URLSearchParams(initData); + const userJson = params.get('user'); + if (!userJson) return null; + const user = JSON.parse(decodeURIComponent(userJson)); + return user.id != null ? String(user.id) : null; + } catch { + return null; + } +} + +const TG_USER_ID_KEY = 'tg_user_id'; + +/** + * Detect Telegram account switch and clear stale auth data. + * + * Telegram Mini App WebView shares localStorage across accounts on the same device + * (confirmed bug on Desktop, likely on mobile too). This means that when user A logs in, + * then user B opens the same Mini App, user A's refresh_token persists in localStorage. + * + * The old approach stored initData in sessionStorage for comparison, but sessionStorage + * is cleared on tab close — so the comparison never triggers between separate openings. + * + * Fix: store the Telegram user ID in localStorage (survives tab close) and compare it + * with the fresh initData on every app launch. + */ export function clearStaleSessionIfNeeded(freshInitData: string | null): void { if (!freshInitData) return; try { - const stored = sessionStorage.getItem(TOKEN_KEYS.TELEGRAM_INIT); + const currentTgUserId = extractTelegramUserId(freshInitData); - if (stored && stored !== freshInitData) { - // New Telegram session (different user) — clear all auth tokens + // PRIMARY CHECK: compare Telegram user ID stored in localStorage (survives tab close) + const storedTgUserId = localStorage.getItem(TG_USER_ID_KEY); + if (storedTgUserId && currentTgUserId && storedTgUserId !== currentTgUserId) { + // Account switch detected — purge all auth data sessionStorage.removeItem(TOKEN_KEYS.ACCESS); sessionStorage.removeItem(TOKEN_KEYS.REFRESH); sessionStorage.removeItem(TOKEN_KEYS.USER); localStorage.removeItem(TOKEN_KEYS.REFRESH); + localStorage.removeItem('cabinet-auth'); + } + + // SECONDARY CHECK: same-session switch (initData changed without tab close) + const storedInitData = sessionStorage.getItem(TOKEN_KEYS.TELEGRAM_INIT); + if (storedInitData && storedInitData !== freshInitData) { + sessionStorage.removeItem(TOKEN_KEYS.ACCESS); + sessionStorage.removeItem(TOKEN_KEYS.REFRESH); + sessionStorage.removeItem(TOKEN_KEYS.USER); + localStorage.removeItem(TOKEN_KEYS.REFRESH); + localStorage.removeItem('cabinet-auth'); + } + + // Persist current Telegram user ID for future comparisons + if (currentTgUserId) { + localStorage.setItem(TG_USER_ID_KEY, currentTgUserId); } sessionStorage.setItem(TOKEN_KEYS.TELEGRAM_INIT, freshInitData); localStorage.removeItem(TOKEN_KEYS.TELEGRAM_INIT); } catch { - // Storage недоступен + // Storage not available } }