Перейти к содержанию

Данные запуска приложения

Внутренняя навигация уже принимает только известные ключи курсов. При открытии Telegram может передать контекст: пользователя, время, параметр ссылки и другие поля. Теперь разделим два представления запуска — raw initData и удобный initDataUnsafe. Результат урока — понятный путь исходной строки на сервер и ограниченное использование подсказки имени в интерфейсе. Наличие данных не считается доказательством входа.

initData является строкой в формате query string. initDataUnsafe — подготовленный SDK объект с удобными полями. Название Unsafe не означает, что ими вообще запрещено пользоваться; оно предупреждает о границе доверия. Например, имя можно показать как временную подсказку, не выдавая права. Telegram прямо требует проверять initData на сервере перед использованием для доверенного контекста. Мы сохраним это разделение в именах функций.

Строка и объект имеют разные задачи

Raw строка нужна для проверки подписи. Если разобрать её в объект, отсортировать JSON пользователя и заново сериализовать, получится другое представление байтов. Сервер должен получить исходную initData, а не красивый клиентский объект. Поэтому не собираем новую строку из initDataUnsafe.user. Даже если значения выглядят одинаковыми, подпись относится к точному декодированному содержимому полей, которое сервер восстановит по правилам протокола.

Объект удобен для неавторитетного интерфейса. Создадим полный модуль launch.js; он сохраняет raw без trim и отдельно возвращает подсказку имени. Поля module не следует печатать целиком в console или передавать аналитику:

export function readLaunch(host) {
  const raw = typeof host?.initData === 'string' ? host.initData : '';
  const firstName = host?.initDataUnsafe?.user?.first_name;
  return Object.freeze({
    raw,
    displayNameHint: typeof firstName === 'string' ? firstName.slice(0, 100) : ''
  });
}

Слова displayNameHint специально не обещают подтверждённого аккаунта. Если интерфейс показывает такую подсказку, используйте textContent, не innerHTML. Ограничение длины помогает маленькому экрану, но не является проверкой личности. Сервер не принимает это поле как источник идентификатора. В минимальную читающую оболочку мы его не включаем автоматически: публичный каталог не нуждается в персонализации, пока нет подтверждённого плана.

Договор обмена запуском

Клиент позднее отправит JSON {initData: launch.raw} на собственный HTTPS endpoint /api/session. Bot token в теле отсутствует. Сервер проверит формат, подпись и auth_date, затем решит, подходит ли запуск нашей модели. На этом этапе он получает Telegram ID и контекст, а не заранее доверенный пользовательский объект от браузера. Конкретная функция HMAC будет полной в следующем уроке; HTTP-адаптер и хранилище сессий описываются отдельно.

При пустой raw строке приложение не подставляет фальшивое значение и не выполняет обмен. Оно остаётся в режиме публичного каталога. Некоторые режимы запуска имеют другой набор данных; мы выбрали основное mini app и прямую ссылку. Этот выбор не превращает поле user в безусловно присутствующее при любом открытии. Если серверу нужен пользователь, отсутствие такого контекста является понятным отказом сценария, а не поводом принять число из query-параметра.

Сырой initData может содержать пользовательские сведения. Не помещайте его в адрес GET запроса: история, логи и другие промежуточные системы могут сохранить строку. HTTPS защищает передачу, но не очищает эти места хранения. Для обмена выбираем POST с ограниченным телом и серверным договором обработки. Никакой endpoint здесь не запускается; маршрут пока является контрактом проекта, а не действующим API на домене ProfessorWeb.

Параметр открытия

start_param помогает выбрать публичный сценарий, например направление JavaScript. Он не является командой произвольного выполнения и не разрешает читать чужой ресурс. Пользователь способен выбрать ссылку открытия, поэтому даже подтверждённое происхождение параметра не превращает его содержание в capability. Сервер или клиент должны разобрать ограниченный формат и сопоставить курс известному каталогу. Навигационный модуль прошлого урока уже отвергает неизвестный ключ.

У ссылки может быть GET-параметр tgWebAppStartParam, который помогает быстро показать начальную оболочку. Он не заменяет подписанный контекст для серверного решения. Если два представления расходятся, важна политика приложения: публичное чтение может показать подсказку, а доверенная операция опирается только на проверенный сервером набор. Не выбирайте «самое удобное» значение для владельца или оплаты. В этой серии контекст будет использоваться исключительно для известного курса.

Изменяемый клиент и повтор строки

Рассмотрим ошибочный JSON: клиент отправляет {telegramUserId:123, authenticated:true} и сервер принимает его. Это обычные поля, которые можно изменить вне интерфейса. Добавление рядом original initData не помогает, если сервер продолжает использовать неподтверждённое число вместо своего результата проверки. Правильный серверный код возвращает отдельный verified context и извлекает owner именно из него. Схожесть имён должна отражать доверие: raw input и verified identity не являются взаимозаменяемыми объектами.

Подпись также не означает одноразовость. Приложение может повторить одну строку в течение допустимого времени. Само имя query_id не следует считать нашей собственной серверной сессией или универсальным ключом идемпотентности всех операций. Оно связано с конкретными возможностями Telegram и может отсутствовать. Для сохранения плана будут отдельные session token, operationId и version, каждый со своей задачей. Это исключает бесконечное повторение initData на каждой кнопке как готовой системы доступа.

Срок жизни и журнал

Чем дольше открыта страница, тем старее исходный auth_date. Доверенный обмен должен иметь согласованное окно свежести, иначе браузер сможет повторять давно полученный запуск. После собственного обмена сессия имеет свой срок жизни. Её истечение не исправляется ручным изменением auth_date в клиенте: подпись перестанет совпадать. Интерфейс сообщает о необходимости нового подтверждения, сохраняя несекретный draft по отдельной политике, вместо подделки контекста.

Для диагностики записывайте код отказа и внутренний request ID, а не полный объект launch. Даже в учебной среде привычка логировать всё быстро переносится в рабочий код. В архиве нет настоящего initData, username, bot token или экспортов пользователей. Ожидаемые сценарии обозначают наличие, отсутствие и повреждение строки без персональных сведений. Они не подтверждают успешное открытие настоящего бота.

Ожидаемый итог — исходная строка хранится отдельно от display hint и никогда не становится авторизацией только по факту наличия. Браузер, Telegram и обмен /api/session здесь не запускались. Следующий урок проверит подпись и время полной серверной функцией, сохранив тот же raw вход и не передавая секрет обратно в веб-интерфейс.

Оглавление серии