Legan Studio
Все статьи
~ 5 мин чтения

Биометрия в Telegram Mini App: Face ID и Touch ID

Как использовать BiometricManager в Mini App: запрос разрешения, аутентификация по Face ID/Touch ID, fallback и хранение токена в безопасном анклаве.

  • Telegram
  • Mini App
  • биометрия
  • безопасность

BiometricManager — это API Telegram Mini Apps, через которое веб-приложение внутри Telegram может попросить устройство аутентифицировать пользователя по Face ID, Touch ID или Android-биометрии. Это закрывает огромный класс задач: вход в банковский кабинет, подтверждение платежа, разблокировка приватных фич — без пароля и без сервера сессий.

Разберём, как включить BiometricManager, как корректно запросить разрешение, как хранить биометрический токен и как сочетать с серверной аутентификацией.

Что умеет BiometricManager

Доступен с Bot API 7.2 (2024). Объект Telegram.WebApp.BiometricManager со свойствами:

  • isInited — инициализирован ли менеджер.
  • isBiometricAvailable — поддерживает ли устройство биометрию.
  • biometricType"finger" | "face" | "unknown".
  • isAccessRequested — спрашивали ли уже разрешение.
  • isAccessGranted — дано ли разрешение.
  • isBiometricTokenSaved — лежит ли токен в безопасном хранилище.
  • deviceId — уникальный ID устройства (для серверной верификации).

И методы:

  • init(callback) — инициализация.
  • requestAccess(params, callback) — попросить разрешение.
  • authenticate(params, callback) — аутентифицировать.
  • updateBiometricToken(token, callback) — сохранить/обновить токен.
  • openSettings() — открыть настройки биометрии устройства.

Базовый сценарий

const bm = window.Telegram.WebApp.BiometricManager;

bm.init(() => {
  if (!bm.isBiometricAvailable) {
    showFallback("Биометрия не поддерживается, войдите паролем");
    return;
  }
  if (!bm.isAccessGranted) {
    bm.requestAccess(
      { reason: "Для быстрого входа в личный кабинет" },
      (granted) => {
        if (granted) loginWithBiometry();
      }
    );
  } else {
    loginWithBiometry();
  }
});

function loginWithBiometry() {
  bm.authenticate(
    { reason: "Подтвердите вход" },
    (success, token) => {
      if (success && token) {
        api.loginWithBioToken(token).then((session) => {
          /* успешный вход */
        });
      }
    }
  );
}

token — это произвольная строка, которую вы один раз сохранили через updateBiometricToken. Она не криптографически проверяема сама по себе, а служит ключом для серверной сессии.

Сохранение токена при первом входе

Логика:

  1. Юзер первый раз вошёл по паролю или OTP.
  2. Сервер выдал refresh-токен (long-lived, с device-binding).
  3. Mini App сохраняет refresh-токен через updateBiometricToken.
  4. На последующих заходах вызывает authenticate → получает токен → отдаёт серверу → сервер выдаёт access-токен.
async function onFirstLogin(refreshToken: string) {
  await new Promise<void>((resolve, reject) =>
    bm.updateBiometricToken(refreshToken, (ok) =>
      ok ? resolve() : reject()
    )
  );
}

Длина токена ограничена 1024 символами. JWT с парой клеймов влезает с запасом.

Серверная верификация

bm.deviceId — уникальный ID устройства в контексте бота. Привязывайте refresh-токен к нему на бэкенде:

@router.post("/auth/biometric")
async def biometric_login(payload: BiometricPayload, request: Request):
    token = payload.token
    device_id = payload.device_id
    user = await sessions.find_by_token_and_device(token, device_id)
    if not user:
        raise HTTPException(401, "invalid_token")
    access = jwt.encode({"sub": user.id, "exp": now()+3600}, SECRET)
    return {"access_token": access}

Если злоумышленник украл токен, но логинится с другого device_id — сервер отклоняет.

Fallback: что делать без биометрии

Не у всех есть Face ID, и не все хотят разрешать. Сценарии fallback:

СитуацияЧто показать
!isBiometricAvailableТолько пароль/OTP, биометрию не предлагаем
!isAccessGranted после requestAccessКнопка «Включить позже в настройках» + пароль
authenticate(success=false)«Не получилось — войдите паролем»
bm.openSettings()Если юзер хочет включить заново

Никогда не блокируйте вход полностью на биометрии — это поломает UX на десктопе.

Re-auth для критичных операций

Для перевода денег, смены пароля, удаления аккаунта — повторная биометрия даже в активной сессии:

async function confirmTransfer(amount: number) {
  const ok = await new Promise<boolean>((res) =>
    bm.authenticate(
      { reason: `Перевод ${amount} ₽` },
      (success) => res(success)
    )
  );
  if (!ok) return;
  await api.confirmTransfer(amount);
}

Юзер видит нативный диалог Face ID с вашим reason — это и UX-понятно, и юридически фиксирует согласие.

Связь с initData

initData идентифицирует юзера в момент открытия Mini App. Биометрия — дополнительный фактор для критичных действий. Полная цепочка авторизации:

  1. Открытие Mini App → initData валидируется на бэкенде → выдаётся access-токен (короткий, 5–15 мин).
  2. Для платежей и приватных фич — biometric authenticate → подтверждение на сервере → выдаётся elevated-токен на 60 секунд.
  3. Действие выполняется с elevated-токеном, который тут же сжигается.

Лимиты и нюансы

  • Биометрия доступна только в нативных клиентах Telegram (iOS, Android, macOS Desktop). В вебе и Linux — isBiometricAvailable = false.
  • Запрос разрешения можно делать только один раз — повторный requestAccess после denied не покажет диалог; нужен openSettings.
  • Токен сбрасывается, если юзер очистил данные Telegram, переустановил приложение или сменил биометрию устройства (новые отпечатки, перенастройка Face ID).
  • deviceId стабилен в рамках устройства, но может смениться после переустановки Telegram.

Топ-5 ошибок

  1. Не вызывают bm.init() перед использованием — все методы возвращают ошибку.
  2. Сохраняют в updateBiometricToken access-токен (короткоживущий) — сессия отваливается через час.
  3. Не привязывают токен к deviceId на бэкенде — токен переносится между устройствами.
  4. Делают биометрию обязательной — юзеры на вебе не могут войти.
  5. Игнорируют success=false в authenticate — баг в логе, фрустрация у юзера.

Итого

BiometricManager превращает Mini App в полноценное безопасное приложение: вход за полсекунды без паролей, повторная аутентификация на критичных действиях, серверная верификация по deviceId. Технически нужны 50 строк кода, серверный эндпоинт /auth/biometric и продуманный fallback. Это даёт +20–40% конверсии в активных юзеров, потому что барьер «введите пароль» больше не возникает каждый день.

Частые вопросы

Что произойдёт, если юзер сменит Face ID на iPhone?

Telegram аннулирует сохранённый биометрический токен — следующий authenticate вернёт success=false, и isBiometricTokenSaved станет false. Это защита от ситуации, когда злоумышленник зарегистрировал свой отпечаток в чужом устройстве. Mini App должен предложить заново войти по паролю и сохранить новый токен.

Биометрия работает на десктопе?

В Telegram Desktop для macOS — да, через Touch ID. На Windows — пока нет (на момент 2026 года Windows Hello не интегрирован). На Linux — нет. Для веб-клиентов (web.telegram.org) — нет. Mini App должен корректно проверять isBiometricAvailable и предлагать пароль на платформах без поддержки.

Можно ли узнать, что биометрия не прошла из-за неверного пальца?

Нет, authenticate отдаёт только success: boolean. Конкретная причина (отпечаток не распознан, юзер отменил, биометрия заблокирована после 5 неудач) скрыта внутри ОС. Для UX это нормально: показываете «Не получилось, попробуйте ещё раз или войдите паролем».

Как часто запрашивать биометрию?

Для входа — один раз в N дней (обычно 7–30). Для критичных операций (платёж, смена пароля) — каждый раз. Внутри сессии не дёргайте на каждый клик — это утомляет. Хорошее правило: биометрия = «согласие на действие», access-токен = «активная сессия».

Что хранить в биометрическом токене?

Refresh-токен с длительным TTL (90+ дней), привязанный на сервере к device_id и user_id. JWT с минимальными клеймами или просто случайная 256-битная строка-ключ к серверной сессии. Никогда не кладите туда персональные данные — токен может попасть в логи устройства при отладке.

Можно ли использовать биометрию для подписи платежа в Stars?

Технически да: вызываете authenticate перед sendInvoice, и только при успехе показываете invoice. Юридически это даёт дополнительный уровень неоспоримости транзакции — пользователь физически подтвердил намерение биометрией. Полезно для крупных сумм (от 5000 ₽).

Что делать, если юзер отказал в доступе к биометрии?

requestAccess нельзя показать второй раз — диалог системный и помнит ответ. Покажите кнопку «Включить биометрию в настройках» с вызовом bm.openSettings() — она открывает соответствующий раздел Telegram, где юзер может изменить решение. И всегда оставляйте альтернативу — пароль или OTP.