Telegram-авторизация на сайте: как это работает — Login Widget, OIDC, бот-флоу и Mini App
Основной чат
Чат для вайбкодеров: новости, гайды, поиск исполнителей, маркетплейс и разбор реальных кейсов.
Обновлено: июнь 2026. Включает новый OIDC-флоу через oauth.telegram.org, появившийся в 2025 году.
Вы добавили на сайт кнопку «Войти через Telegram», скопировали скрипт с виджетом — и думаете, что готово. Но если вы не написали бэкенд, который проверяет HMAC-подпись, — вы только что создали дыру: любой желающий может передать произвольный user_id и зайти под чужим аккаунтом.
Авторизация через Telegram кажется простой снаружи. Внутри у неё несколько режимов, нетривиальная криптография и ряд архитектурных решений, которые влияют на безопасность и UX. В этой статье — полный разбор: как устроен каждый способ, как выбрать нужный, как правильно проверять подпись и хранить сессии.
Четыре способа авторизоваться через Telegram
Перед деталями — карта вариантов. Телеграм не использует OAuth 2.0 в классическом виде — он «любит изобретать велосипеды», как точно замечают разработчики в Рунете. Поэтому вместо одного стандартного флоу у нас четыре разных механизма:
| Способ | Когда появился | Для чего подходит | Сложность |
|---|---|---|---|
| Login Widget | Февраль 2018 | Сайты с бэкендом, классический «Войти через Telegram» | Средняя |
| OIDC через oauth.telegram.org | 2025 | Современные приложения, совместимость со стандартом | Средняя |
| Бот с одноразовой ссылкой | Всегда работал | Россия (виджет блокируется), корпоративные сценарии | Низкая |
| Mini App initData | 2022+ | Приложения внутри Telegram | Средняя |
Не существует «лучшего» — есть подходящий под сценарий.
Login Widget: классика и её проблемы
Как работает
Login Widget — это JavaScript-скрипт, который Telegram предоставил в 2018 году. Вы вставляете его на страницу, пользователь видит кнопку «Войти через Telegram», кликает — и происходит следующее:
- Браузер открывает всплывающее окно или редирект на серверы Telegram.
- Если пользователь первый раз — Telegram просит номер телефона и отправляет код подтверждения в приложение.
- Если пользователь уже авторизован в веб-версии Telegram — ничего вводить не нужно.
- Telegram формирует объект с данными пользователя и криптографической подписью и передаёт его на ваш сайт — либо через
callback_url(редирект), либо через JavaScript-колбэк.
Данные, которые приходят:
{
"id": 123456789,
"first_name": "Иван",
"last_name": "Иванов",
"username": "ivan_ivanov",
"photo_url": "https://t.me/i/userpic/...",
"auth_date": 1749300000,
"hash": "abc123def456..."
}
Встраивание виджета
<!-- Режим редиректа: данные придут на callback_url как GET-параметры -->
<script
async
src="https://telegram.org/js/telegram-widget.js?22"
data-telegram-login="YourBotUsername"
data-size="large"
data-auth-url="https://your-site.ru/auth/telegram/callback"
data-request-access="write">
</script>
<!-- Режим колбэка: данные придут в JavaScript-функцию -->
<script
async
src="https://telegram.org/js/telegram-widget.js?22"
data-telegram-login="YourBotUsername"
data-size="large"
data-onauth="onTelegramAuth(user)"
data-request-access="write">
</script>
<script>
function onTelegramAuth(user) {
// НЕ доверяйте этим данным без проверки на бэкенде!
fetch('/auth/telegram', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
});
}
</script>
Проверка подписи на бэкенде: это обязательно
Данные, которые пришли от виджета, нельзя считать доверенными без проверки. Telegram подписывает их HMAC-SHA256 — ключ строится из токена бота. Алгоритм проверки:
- Взять все поля из объекта, кроме
hash. - Отсортировать по алфавиту, соединить через
\n:auth_date=...\nfirst_name=...\nid=.... - Вычислить
secret_key = HMAC-SHA256("WebAppData", bot_token)— нет, стоп. Для Login Widget по-другому:secret_key = SHA256(bot_token). - Вычислить
HMAC-SHA256(data_check_string, secret_key). - Сравнить результат с полем
hash. Должны совпасть. - Проверить
auth_date— данные не должны быть старше 24 часов.
Node.js:
const crypto = require('crypto');
function verifyTelegramAuth(data, botToken) {
const { hash, ...rest } = data;
// Формируем строку для проверки
const dataCheckString = Object.keys(rest)
.sort()
.map(key => `${key}=${rest[key]}`)
.join('\n');
// Ключ для Login Widget: SHA256 от токена бота
const secretKey = crypto
.createHash('sha256')
.update(botToken)
.digest();
// Вычисляем HMAC
const computedHash = crypto
.createHmac('sha256', secretKey)
.update(dataCheckString)
.digest('hex');
// Проверяем подпись
if (computedHash !== hash) {
throw new Error('Недействительная подпись Telegram');
}
// Проверяем свежесть: не старше 24 часов
const authDate = parseInt(rest.auth_date, 10);
const now = Math.floor(Date.now() / 1000);
if (now - authDate > 86400) {
throw new Error('Данные авторизации устарели');
}
return true;
}
// Использование в Express
app.post('/auth/telegram', async (req, res) => {
try {
verifyTelegramAuth(req.body, process.env.BOT_TOKEN);
// Подпись верна — ищем или создаём пользователя
const user = await upsertUser({
telegramId: req.body.id,
firstName: req.body.first_name,
lastName: req.body.last_name,
username: req.body.username,
});
// Выдаём сессию или JWT
req.session.userId = user.id;
res.json({ ok: true });
} catch (err) {
res.status(401).json({ error: err.message });
}
});
Python:
import hashlib
import hmac
import time
def verify_telegram_auth(data: dict, bot_token: str) -> bool:
received_hash = data.get('hash')
if not received_hash:
raise ValueError('Отсутствует hash')
# Строим строку для проверки
data_check = {k: v for k, v in data.items() if k != 'hash'}
data_check_string = '\n'.join(
f'{k}={v}' for k, v in sorted(data_check.items())
)
# SHA256 от токена бота как ключ
secret_key = hashlib.sha256(bot_token.encode()).digest()
# HMAC-SHA256
computed_hash = hmac.new(
secret_key,
data_check_string.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(computed_hash, received_hash):
raise ValueError('Недействительная подпись')
# Проверка времени
auth_date = int(data.get('auth_date', 0))
if time.time() - auth_date > 86400:
raise ValueError('Данные авторизации устарели')
return True
Проблема виджета в России
В России Telegram Login Widget не работает, поэтому авторизацию через Telegram проще реализовать с помощью бота: при отправке команды бот вызывает вебхук, передавая имя пользователя, в ответ отправляется одноразовая ссылка для входа. Бота нужно запускать на зарубежном хостинге, так как с него должен быть доступен api.telegram.org, — пользователь при этом может находиться где угодно.
Это не баг — это следствие блокировки Telegram в 2018–2020 годах. Виджет обращается к серверам Telegram напрямую из браузера, и даже если блокировка снята на уровне провайдеров, DNS и CDN могут создавать проблемы. Бот-флоу не зависит от доступности виджетных скриптов.
OIDC через oauth.telegram.org: новый стандартный путь
В 2025 году Telegram выпустил полноценный OpenID Connect флоу — через oauth.telegram.org. Это важное обновление: теперь авторизация через Telegram совместима со стандартом, который поддерживают большинство фреймворков и библиотек.
В официальной документации теперь описан полноценный OpenID Connect через oauth.telegram.org: с JWKS, JWT и claims. PoC умеет логинить пользователя через новый OIDC, держит cookie-сессию для HTML-страниц и отдаёт пару access + refresh токенов для JSON API.
Как работает OIDC-флоу
Стандартный Authorization Code Flow:
Пользователь → ваш сайт → oauth.telegram.org/auth?client_id=...
→ Telegram подтверждает личность
→ Редирект на ваш callback с code
→ Ваш бэкенд обменивает code на токены через POST /token
→ Получаете JWT с данными пользователя
→ Проверяете подпись JWT через JWKS
# Пример конфигурации для python-social-auth / authlib
TELEGRAM_OIDC_CONFIG = {
'issuer': 'https://oauth.telegram.org',
'authorization_endpoint': 'https://oauth.telegram.org/auth',
'token_endpoint': 'https://oauth.telegram.org/token',
'jwks_uri': 'https://oauth.telegram.org/.well-known/jwks.json',
}
# Scope: openid profile
Сессии: cookie vs JWT
Cookie-сессия нужна, чтобы HTML-страницы «помнили» пользователя между запросами. JWT-пара — для JSON API (мобилка/SPA).
Классическое правило: не смешивайте подходы без необходимости. Для server-side рендеринга — httpOnly cookie с session ID. Для SPA и мобильных приложений — JWT access token (короткий TTL, 15–60 минут) + refresh token (длинный TTL, в httpOnly cookie или безопасном хранилище).
// Выдача сессии после успешной авторизации (Express + express-session)
app.get('/auth/telegram/callback', async (req, res) => {
const { code } = req.query;
// Обмениваем code на токены
const tokens = await exchangeCodeForTokens(code);
// Проверяем JWT через JWKS
const userInfo = await verifyJWT(tokens.id_token);
// Создаём или обновляем пользователя
const user = await upsertUser({
telegramId: userInfo.sub,
firstName: userInfo.given_name,
username: userInfo.preferred_username,
});
// Сохраняем в сессии
req.session.userId = user.id;
req.session.save(() => {
res.redirect('/dashboard');
});
});
Когда выбирать OIDC
OIDC через oauth.telegram.org — лучший выбор, если:
- вы строите новое приложение с нуля и хотите стандартный флоу
- ваш фреймворк или библиотека поддерживает OIDC (Next-Auth, Passport.js, Spring Security и т.д.)
- нужна совместимость с другими провайдерами авторизации в одной системе
Виджет остаётся разумным выбором для простых сценариев и уже работающих проектов.
Бот с одноразовой ссылкой: надёжно и работает везде
Этот способ не зависит от виджета, не требует OIDC и работает даже там, где telegram.org недоступен из браузера.
Принцип
Пользователь на сайте → нажимает «Войти через Telegram»
→ Сайт генерирует одноразовый токен (UUID), сохраняет в Redis
→ Показывает кнопку «Открыть бота» со ссылкой t.me/YourBot?start=<token>
→ Пользователь открывает бота, отправляет /start <token>
→ Бот делает запрос к API сайта: «токен X принадлежит пользователю telegram_id Y»
→ Сайт связывает токен с telegram_id, создаёт сессию
→ Страница авторизации опрашивает API каждые 2 секунды, видит сессию — редиректит
Реализация
Генерация токена и отображение кнопки (Node.js/Express):
const { v4: uuidv4 } = require('uuid');
const redis = require('./redis'); // ваш Redis-клиент
app.get('/auth/telegram/init', async (req, res) => {
const token = uuidv4();
// Сохраняем токен на 5 минут, пока пользователь не авторизовался
await redis.setex(`tg_auth:${token}`, 300, JSON.stringify({ status: 'pending' }));
res.json({
token,
botUrl: `https://t.me/${process.env.BOT_USERNAME}?start=${token}`,
});
});
// Опрос статуса авторизации (фронтенд спрашивает каждые 2 сек)
app.get('/auth/telegram/status/:token', async (req, res) => {
const data = await redis.get(`tg_auth:${req.params.token}`);
if (!data) return res.json({ status: 'expired' });
res.json(JSON.parse(data));
});
Обработчик в боте (node-telegram-bot-api):
const TelegramBot = require('node-telegram-bot-api');
const bot = new TelegramBot(process.env.BOT_TOKEN, { polling: true });
bot.onText(/\/start (.+)/, async (msg, match) => {
const token = match[1];
const telegramId = msg.from.id;
// Проверяем, что токен существует и ещё pending
const data = await redis.get(`tg_auth:${token}`);
if (!data || JSON.parse(data).status !== 'pending') {
bot.sendMessage(telegramId, '❌ Ссылка недействительна или устарела.');
return;
}
// Создаём или находим пользователя
const user = await upsertUser({
telegramId,
firstName: msg.from.first_name,
lastName: msg.from.last_name,
username: msg.from.username,
});
// Привязываем токен к пользователю
await redis.setex(
`tg_auth:${token}`,
60, // Ещё 60 секунд, чтобы сайт успел прочитать
JSON.stringify({ status: 'authorized', userId: user.id })
);
bot.sendMessage(telegramId, '✅ Вы успешно вошли на сайт!');
});
Фронтенд: опрос статуса:
async function initTelegramAuth() {
// Получаем токен и ссылку на бота
const { token, botUrl } = await fetch('/auth/telegram/init').then(r => r.json());
// Показываем кнопку
document.getElementById('tg-link').href = botUrl;
document.getElementById('tg-link').style.display = 'block';
// Опрашиваем статус каждые 2 секунды
const interval = setInterval(async () => {
const status = await fetch(`/auth/telegram/status/${token}`).then(r => r.json());
if (status.status === 'authorized') {
clearInterval(interval);
// Финализируем сессию и редиректим
await fetch('/auth/telegram/finalize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
window.location.href = '/dashboard';
}
if (status.status === 'expired') {
clearInterval(interval);
alert('Ссылка устарела. Попробуйте снова.');
}
}, 2000);
}
Mini App и initData: авторизация внутри Telegram
Если ваш продукт — это Telegram Mini App (веб-приложение, открывающееся внутри Telegram), у вас другой механизм авторизации. Telegram при открытии Mini App передаёт в window.Telegram.WebApp.initData строку с данными пользователя и криптографической подписью.
Что такое initData
initData — это URL-encoded строка, которую Telegram прокидывает в Mini App при открытии. В ней содержатся поля: auth_date, user (JSON с id, именем, аватаром), hash (HMAC-подпись от Telegram), и ещё несколько служебных. Главное правило безопасности: никогда не доверяйте initDataUnsafe.user напрямую — это поле клиент может подделать в DevTools. Доверять можно только тому, что прошло проверку HMAC на бэкенде.
Алгоритм проверки initData (отличается от Login Widget!)
Для Mini App ключ строится иначе: HMAC-SHA256("WebAppData", bot_token) — не SHA256, а именно HMAC с литеральной строкой "WebAppData".
const crypto = require('crypto');
function verifyInitData(initDataString, botToken) {
const params = new URLSearchParams(initDataString);
const hash = params.get('hash');
params.delete('hash');
// Сортируем параметры и формируем строку
const dataCheckString = [...params.entries()]
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join('\n');
// Ключ: HMAC-SHA256("WebAppData", bot_token) — НЕ SHA256!
const secretKey = crypto
.createHmac('sha256', 'WebAppData')
.update(botToken)
.digest();
const computedHash = crypto
.createHmac('sha256', secretKey)
.update(dataCheckString)
.digest('hex');
if (computedHash !== hash) {
throw new Error('Недействительная подпись initData');
}
// Проверка времени: обычно допускают 1 час, не 24
const authDate = parseInt(params.get('auth_date'), 10);
if (Date.now() / 1000 - authDate > 3600) {
throw new Error('initData устарела');
}
// Возвращаем данные пользователя
return JSON.parse(params.get('user'));
}
Флоу авторизации в Mini App
// Фронтенд: отправляем initData на бэкенд при старте
const tg = window.Telegram.WebApp;
tg.ready();
async function authenticate() {
const response = await fetch('/auth/miniapp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ initData: tg.initData }),
});
const { token } = await response.json();
// Сохраняем JWT для последующих запросов
localStorage.setItem('auth_token', token);
}
// Бэкенд
app.post('/auth/miniapp', async (req, res) => {
const { initData } = req.body;
try {
const user = verifyInitData(initData, process.env.BOT_TOKEN);
const dbUser = await upsertUser({ telegramId: user.id, ...user });
// Выдаём JWT (короткий TTL — initData можно переиспользовать)
const token = jwt.sign({ userId: dbUser.id }, process.env.JWT_SECRET, {
expiresIn: '7d',
});
res.json({ token });
} catch (err) {
res.status(401).json({ error: err.message });
}
});
Если не проверять подпись — злоумышленник может подделать пользователя. Отсутствие HTTPS критично: Telegram требует HTTPS, но некоторые ставят самоподписанные сертификаты. Весь фронт Mini App работает в браузере Telegram — значит, XSS может быть критичен. Если есть открытые эндпоинты, которые отдают данные без аутентификации — это утечка.
Сессии и безопасность: что важно не пропустить
Cookie-сессии: основные правила
// Правильная конфигурация express-session
app.use(session({
secret: process.env.SESSION_SECRET, // длинная случайная строка
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // JS не имеет доступа к куке
secure: true, // Только HTTPS
sameSite: 'lax', // Защита от CSRF
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 дней
},
store: redisStore, // Храним в Redis, не в памяти
}));
JWT: не хранить в localStorage для критичных данных
// Плохо: localStorage уязвим к XSS
localStorage.setItem('token', jwt);
// Лучше для веба: httpOnly cookie
res.cookie('access_token', jwt, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 15 * 60 * 1000, // 15 минут
});
Для Mini App — localStorage допустим, так как среда более контролируемая, но контекст работы в браузере Telegram всё равно требует осторожности с XSS.
Главные ошибки безопасности
| Ошибка | Последствие | Решение |
|---|---|---|
Нет проверки hash на бэкенде |
Любой может войти под чужим ID | Обязательная HMAC-верификация |
Нет проверки auth_date |
Перехваченные данные работают вечно | Проверяем TTL: 24ч для виджета, 1ч для initData |
Сравнение строк через === |
Timing attack — можно угадать хеш по времени ответа | Используйте crypto.timingSafeEqual |
Хранить bot_token в коде |
Утечка токена = полный контроль бота | Только через переменные среды |
| HTTP вместо HTTPS | Перехват сессионной куки | Только HTTPS, secure: true на cookie |
Доверять initDataUnsafe.user |
XSS или DevTools позволяют подделать | Только initData с HMAC-проверкой на сервере |
// Правильное сравнение хешей (защита от timing attack)
const timingSafeEqual = (a, b) => {
const bufA = Buffer.from(a, 'hex');
const bufB = Buffer.from(b, 'hex');
if (bufA.length !== bufB.length) return false;
return crypto.timingSafeEqual(bufA, bufB);
};
Связь сайта и бота: один пользователь в двух системах
Если у вас одновременно есть сайт и Telegram-бот, ключ связи — telegram_id. Он уникален, не меняется, не зависит от username (username пользователь может сменить).
-- Схема таблицы пользователей
CREATE TABLE users (
id SERIAL PRIMARY KEY,
telegram_id BIGINT UNIQUE NOT NULL, -- главный ключ из Telegram
username VARCHAR(64), -- может меняться
first_name VARCHAR(128),
created_at TIMESTAMPTZ DEFAULT NOW(),
-- ваши бизнес-поля
plan VARCHAR(32) DEFAULT 'free',
email VARCHAR(255)
);
Тогда и сайт, и бот работают с одним user.id из вашей БД, связанным через telegram_id. Пользователь может настроить что-то в боте — изменения сразу видны на сайте, и наоборот.
Сравнение способов: что выбрать
| Критерий | Login Widget | OIDC | Бот-флоу | Mini App initData |
|---|---|---|---|---|
| Работает в России | Нестабильно | Да | Да | Да (внутри Telegram) |
| Сложность реализации | Средняя | Средняя | Низкая | Средняя |
| Стандарт | Нет | OpenID Connect | Нет | Нет |
| Нужен бэкенд | Обязательно | Обязательно | Обязательно | Обязательно |
| Нужен бот | Да (для настройки) | Да | Да (активно) | Да (активно) |
| UX | Хороший | Хороший | Чуть сложнее | Лучший в экосистеме |
| Подходит для SPA | Да | Да | Да | Только внутри TG |
| Подходит для SSR | Да | Да | Да | Только внутри TG |
| Безопасность | HMAC-SHA256 | JWT/JWKS | Одноразовый токен | HMAC-SHA256 (иначе) |
Рекомендации по сценарию
Публичный сайт, аудитория не только в России → Login Widget или OIDC. Виджет проще и быстрее внедрить. OIDC — если уже есть OAuth-инфраструктура.
Сайт с аудиторией преимущественно из России → Бот-флоу с одноразовой ссылкой. Надёжно, не зависит от доступности Telegram-скриптов.
Telegram Mini App → Только initData. Другие методы неуместны внутри экосистемы.
Корпоративный инструмент или закрытое сообщество → Бот-флоу. Можно добавить проверку, что пользователь входит только если telegram_id есть в вайтлисте.
Вайбкодинг и Telegram-авторизация: где ИИ помогает, где нет
Авторизация — та область, где Claude Code или Codex могут написать 80% кода за вас. Но оставшиеся 20% требуют вашего понимания: криптографический алгоритм надо проверить вручную, временные проверки нельзя опустить, хранение секретов нельзя делегировать «напиши как-нибудь».
Хороший промпт для Claude Code:
Реализуй авторизацию через Telegram Login Widget на Express.js.
Требования:
- Проверка HMAC-SHA256 с bot_token из .env
- Проверка auth_date (не старше 24ч)
- Сравнение хешей через crypto.timingSafeEqual
- После успешной проверки: сессия через express-session + Redis
- Upsert пользователя в PostgreSQL по telegram_id
- Эндпоинт GET /auth/telegram/callback для redirect-режима виджета
- Эндпоинт POST /auth/telegram для JavaScript-колбэка
Не используй любые npm-пакеты для проверки Telegram — только встроенный crypto.
Плохой промпт:
Сделай авторизацию через Telegram
Результат второго — часто код без проверки подписи, с hash в строке сравнения через ===, и без проверки времени. Это работающий, но небезопасный код.
Чеклист реализации
Подготовка:
☐ Создан бот через @BotFather, получен токен
☐ В BotFather указан домен сайта (setdomain) — для Login Widget
☐ BOT_TOKEN хранится в переменных среды, не в коде
☐ Сайт работает по HTTPS
Бэкенд:
☐ Реализована HMAC-проверка подписи (SHA256 для виджета, HMAC для initData)
☐ Проверяется auth_date (не старше 24ч для виджета, 1ч для initData)
☐ Сравнение хешей через timingSafeEqual, не через ===
☐ Реализован upsert пользователя по telegram_id (не по username!)
☐ Сессия или JWT выдаётся только после успешной проверки
☐ Cookie: httpOnly, secure, sameSite
Сессии:
☐ Сессии хранятся в Redis или PostgreSQL, не в памяти Node.js
☐ Установлен TTL для сессий
☐ Реализован выход (удаление сессии/инвалидация JWT)
Безопасность:
☐ Нет доверия initDataUnsafe.user без HMAC-проверки
☐ Все API-эндпоинты требуют аутентификации
☐ Логируются попытки неудачной авторизации
Тестирование:
☐ Проверена авторизация в реальном Telegram (не в тестовом клиенте)
☐ Проверен сценарий с устаревшими данными (auth_date > 24ч)
☐ Проверен сценарий с подменой hash
Итог
Авторизация через Telegram — мощный инструмент: пользователь не создаёт новый пароль, не проходит лишних шагов, вы получаете верифицированный identity. Но она работает правильно только если бэкенд проверяет подпись.
Главное правило: данные от Telegram — это только hint. Доверенными они становятся после того, как вы проверили HMAC на сервере своим токеном бота.
Выбор механизма определяется сценарием: Login Widget — классика, OIDC — стандарт будущего, бот-флоу — надёжность для русскоязычной аудитории, initData — если вы уже внутри экосистемы Telegram.
FAQ
Нужен ли отдельный бот для Login Widget? Да, но он используется только как identity-провайдер. Пользователь не обязан писать боту — достаточно что бот существует и в BotFather задан домен вашего сайта.
Чем telegram_id лучше username для хранения в БД?
Username пользователь может сменить в любой момент. telegram_id — постоянный числовой идентификатор, не меняется никогда. Всегда используйте его как первичный ключ связи.
Можно ли реализовать авторизацию без Redis?
Для бот-флоу — да, можно хранить одноразовые токены в PostgreSQL с полем expires_at. Для сессий — рекомендуется Redis или PostgreSQL с индексом по session_id. Хранение сессий в памяти Node.js не переживает перезапуск.
Как отозвать сессию? При Redis-хранении — удаление ключа сессии. При JWT — нужен blacklist (тот же Redis со списком отозванных jti). Без blacklist JWT нельзя отозвать до истечения TTL — поэтому для критичных сценариев используйте короткий TTL (15–60 минут) + refresh token.
Telegram Mini App или обычный сайт — что лучше для авторизации через Telegram? Разные аудитории. Mini App — для пользователей, которые уже в Telegram и хотят остаться в экосистеме. Обычный сайт с Login Widget — для более широкой аудитории. Многие проекты реализуют оба входа.
Данные актуальны на июнь 2026. Telegram продолжает развивать экосистему — проверяйте официальную документацию core.telegram.org и обновления oauth.telegram.org.