From 32091d3648889795d01d78bff933da3a38caa10f Mon Sep 17 00:00:00 2001 From: Fringg Date: Sat, 7 Mar 2026 16:15:20 +0300 Subject: [PATCH] fix: replace deprecated Telegram Login Widget redirect with callback Telegram deprecated the data-auth-url redirect flow, which returns a blank page with "deprecated" text. Switch both TelegramLinkWidget (account linking) and TelegramLoginButton (legacy login) to use data-onauth callback approach that works client-side without redirects. --- src/components/TelegramLoginButton.tsx | 27 +++++++++++++++-- src/pages/ConnectedAccounts.tsx | 42 +++++++++++++++++++++----- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/components/TelegramLoginButton.tsx b/src/components/TelegramLoginButton.tsx index 08ebd8d..037be27 100644 --- a/src/components/TelegramLoginButton.tsx +++ b/src/components/TelegramLoginButton.tsx @@ -106,6 +106,8 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto }, [isOIDC, widgetConfig?.oidc_client_id, widgetConfig?.request_access]); // Legacy widget effect (only when NOT OIDC) + const loginWithTelegramWidget = useAuthStore((s) => s.loginWithTelegramWidget); + useEffect(() => { if (isOIDC || !containerRef.current || !botUsername || !widgetConfig) return; @@ -114,7 +116,25 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto container.removeChild(container.firstChild); } - const redirectUrl = `${window.location.origin}/auth/telegram/callback`; + const callbackName = '__onTelegramWidgetAuth'; + (window as unknown as Record)[callbackName] = async ( + user: Record, + ) => { + try { + await loginWithTelegramWidget({ + id: user.id as number, + first_name: user.first_name as string, + last_name: (user.last_name as string) || undefined, + username: (user.username as string) || undefined, + photo_url: (user.photo_url as string) || undefined, + auth_date: user.auth_date as number, + hash: user.hash as string, + }); + navigate('/'); + } catch { + // Error handled by auth store + } + }; const script = document.createElement('script'); script.src = 'https://telegram.org/js/telegram-widget.js?23'; @@ -122,7 +142,7 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto script.setAttribute('data-size', widgetConfig.size); script.setAttribute('data-radius', String(widgetConfig.radius)); script.setAttribute('data-userpic', String(widgetConfig.userpic)); - script.setAttribute('data-auth-url', redirectUrl); + script.setAttribute('data-onauth', `${callbackName}(user)`); if (widgetConfig.request_access) { script.setAttribute('data-request-access', 'write'); } @@ -131,11 +151,12 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto container.appendChild(script); return () => { + delete (window as unknown as Record)[callbackName]; while (container.firstChild) { container.removeChild(container.firstChild); } }; - }, [isOIDC, botUsername, widgetConfig]); + }, [isOIDC, botUsername, widgetConfig, loginWithTelegramWidget, navigate]); if (!botUsername || botUsername === 'your_bot') { return ( diff --git a/src/pages/ConnectedAccounts.tsx b/src/pages/ConnectedAccounts.tsx index 4217dbf..977e67e 100644 --- a/src/pages/ConnectedAccounts.tsx +++ b/src/pages/ConnectedAccounts.tsx @@ -27,6 +27,10 @@ export const LINK_TELEGRAM_STATE_KEY = 'link_telegram_state'; /** Compact Telegram Login Widget for account linking (browser only). */ function TelegramLinkWidget() { const containerRef = useRef(null); + const navigate = useNavigate(); + const { showToast } = useToast(); + const { t } = useTranslation(); + const queryClient = useQueryClient(); const botUsername = import.meta.env.VITE_TELEGRAM_BOT_USERNAME || ''; useEffect(() => { @@ -37,29 +41,53 @@ function TelegramLinkWidget() { container.removeChild(container.firstChild); } - // Generate CSRF state token and store in sessionStorage - const csrfState = crypto.randomUUID(); - sessionStorage.setItem(LINK_TELEGRAM_STATE_KEY, csrfState); - - const redirectUrl = `${window.location.origin}/auth/link/telegram/callback?csrf_state=${encodeURIComponent(csrfState)}`; + // Global callback invoked by the Telegram Login Widget + const callbackName = '__onTelegramLinkAuth'; + (window as unknown as Record)[callbackName] = async ( + user: Record, + ) => { + try { + const response = await authApi.linkTelegram({ + id: user.id as number, + first_name: user.first_name as string, + last_name: (user.last_name as string) || undefined, + username: (user.username as string) || undefined, + photo_url: (user.photo_url as string) || undefined, + auth_date: user.auth_date as number, + hash: user.hash as string, + }); + if (response.merge_required && response.merge_token) { + navigate(`/merge/${response.merge_token}`, { replace: true }); + } else { + queryClient.invalidateQueries({ queryKey: ['linked-providers'] }); + showToast({ type: 'success', message: t('profile.accounts.linkSuccess') }); + } + } catch (err: unknown) { + showToast({ + type: 'error', + message: getErrorDetail(err) || t('profile.accounts.linkError'), + }); + } + }; const script = document.createElement('script'); script.src = 'https://telegram.org/js/telegram-widget.js?23'; script.setAttribute('data-telegram-login', botUsername); script.setAttribute('data-size', 'small'); script.setAttribute('data-radius', '8'); - script.setAttribute('data-auth-url', redirectUrl); + script.setAttribute('data-onauth', `${callbackName}(user)`); script.setAttribute('data-request-access', 'write'); script.async = true; container.appendChild(script); return () => { + delete (window as unknown as Record)[callbackName]; while (container.firstChild) { container.removeChild(container.firstChild); } }; - }, [botUsername]); + }, [botUsername, navigate, showToast, t, queryClient]); if (!botUsername) { return null;