GameDev Guide
Глава 912 мин чтенияПроверено: 5 сентября 2026 г.

Авторизация игрока: Яндекс ID, гостевой режим и требование 1.2

Научитесь получать объект Player, проверять статус авторизации, корректно открывать окно входа через Яндекс ID и делать гостевой режим, который сохраняет прогресс и проходит модерацию.

Содержание главы

Авторизация в Яндекс Играх устроена не так, как на большинстве платформ. Здесь нет своей регистрации, нет формы «введите email», и главное — вход не обязателен. Игрок может открыть игру, пройти десять уровней, закрыть вкладку, вернуться завтра и увидеть свой прогресс, так и не авторизовавшись. Это не побочный эффект, а требование платформы: игра, которая заставляет войти в аккаунт, отклоняется на модерации.

В этой главе разберём объект Player: как его получить, как отличить гостя от авторизованного пользователя, как корректно открыть окно входа и что именно проверяет модерация в пункте 1.2. Все факты сверены с официальной документацией в сентябре 2026 года — ссылки на источники в шапке главы.

Что даёт авторизация, а что работает и без неё

Платформа сама сохраняет игровой прогресс и авторизованных, и неавторизованных игроков — облачные сохранения через player.setData() работают в обоих случаях. Поэтому «сохранение прогресса» само по себе плохой аргумент за вход: гость его тоже получает, просто привязанный к текущему браузеру.

Реальная выгода от авторизации, о которой честно можно написать в интерфейсе:

  • прогресс переносится между устройствами и браузерами, а не живёт в одном;
  • купленные предметы и улучшения доступны везде, а не только там, где их купили (для игр с инап-покупками облачные сохранения обязательны по пункту 1.13.3);
  • становятся доступны лидерборды — leaderboards.setScore() и getPlayerEntry() требуют авторизации;
  • в игре появляются имя и аватар игрока.

Без авторизации остаётся доступным: сам факт запуска, уникальный идентификатор игрока, сохранение и чтение данных, реклама, чтение таблицы лидеров через getEntries().

Требование 1.2: правила, из-за которых игру отклоняют

Пункт 1.2 требований формулируется коротко: для запуска и использования игры не нужна регистрация или авторизация на сторонних сервисах, допустима только авторизация с Яндекс ID. Дальше идут два подпункта, которые и ловят модераторы.

ПравилоПравильноНеправильно
1.2 Основные функции без входаИграть можно сразу, вход опционаленИгра начинается только после авторизации
1.2 Только Яндекс IDКнопка ведёт на ysdk.auth.openAuthDialog()Своя форма регистрации, вход через VK, Google, Telegram
1.2.1 Осознанное действиеВход открывается по кнопке «Войти»Окно входа открывается по кнопке «Лидерборд» или «Оценить игру»
1.2.1 Объяснённая выгодаРядом с кнопкой написано, что даёт входКнопка «Войти» без пояснений
1.2.1 Право отказатьсяЕсть кнопка «Позже» / «Играть без входа»Модальное окно без выхода
1.2 Не повторятьсяПосле входа предложение исчезаетПредложение авторизоваться показывается уже вошедшему игроку
1.2.2 Гостевой прогрессГость тоже сохраняетсяПрогресс сохраняется только авторизованным

Важно

Самая частая ошибка — вешать openAuthDialog() на кнопку лидерборда. Логика понятна: рекорд без авторизации не отправить. Но с точки зрения пользователя он нажал «Таблица рекордов», а получил экран входа в Яндекс. По документации это прямой пример неправильной реализации 1.2.1. Правильный путь: показать таблицу (getEntries() работает без авторизации), а под ней — отдельную кнопку «Войти, чтобы попасть в рейтинг».

Объект Player: ysdk.getPlayer()

Объект Player — точка входа ко всему, что связано с игроком: идентификатор, профиль, сохранения. Получается он одним асинхронным вызовом после инициализации SDK.

const ysdk = await YaGames.init();
const player = await ysdk.getPlayer();

При инициализации платформа передаёт в игру идентификатор пользователя (всем), а имя и аватар — только авторизованным. Доступ к персональным данным зависит ещё и от настроек профиля самого пользователя: если он запретил их передачу, в ответе будет только идентификатор, даже когда игрок авторизован. Поэтому имя из getName() всегда нужно подстраховывать запасным значением.

Единственная опция — signed

getPlayer() принимает ровно один документированный параметр — { signed: true }. Он нужен, если вы проверяете подлинность игрока на своём сервере (об этом в конце главы).

// Клиентская игра без своего сервера — параметры не нужны.
const player = await ysdk.getPlayer();

// Игра со своим сервером: в объекте появится player.signature.
const signedPlayer = await ysdk.getPlayer({ signed: true });

Частая ошибка

В старых туториалах и в коде, который генерируют языковые модели, часто встречается ysdk.getPlayer({ scopes: true }) — якобы «запросить доступ к имени и аватару». Слова scopes в актуальной документации SDK нет вообще (проверено в русской и английской версиях в сентябре 2026 года). Считайте этот параметр легаси: он ничего не гарантирует, доступ к профилю определяется фактом авторизации и настройками пользователя. Пишите getPlayer() или getPlayer({ signed: true }) — других вариантов документация не описывает.

Проверка статуса и окно входа

Статус проверяется синхронным методом player.isAuthorized() — он возвращает true или false. Окно входа открывается методом ysdk.auth.openAuthDialog(), который возвращает промис.

Ключевой момент, который ломает половину реализаций: после успешной авторизации старый объект Player не становится авторизованным. Он остаётся гостевым навсегда. Нужно запросить getPlayer() заново и заменить ссылку.

let player = await ysdk.getPlayer();

if (!player.isAuthorized()) {
  try {
    await ysdk.auth.openAuthDialog();
    // Обязательный шаг: перечитываем игрока.
    player = await ysdk.getPlayer();
  } catch (e) {
    // Пользователь закрыл окно или отказался — это штатный сценарий,
    // игра должна продолжаться в гостевом режиме.
  }
}

Если пропустить повторный вызов, player.isAuthorized() продолжит возвращать false, getName() останется пустым, а setScore() будет падать — и выглядеть это будет как «SDK не работает».

Готовый модуль игрока

Соберём всё в один файл, который подключается обычным тегом <script src="player.js"></script> — без сборки и без зависимостей.

// player.js — состояние игрока и авторизация.

const PlayerAPI = (() => {
  let ysdk = null;
  let player = null;

  /** Вызывается один раз при старте игры, после YaGames.init(). */
  async function init(sdk) {
    ysdk = sdk;

    try {
      player = await ysdk.getPlayer();
    } catch (e) {
      // Объект Player недоступен — игра должна работать и так.
      console.warn('getPlayer() недоступен:', e);
      player = null;
    }

    return getProfile();
  }

  function isAuthorized() {
    return Boolean(player && player.isAuthorized());
  }

  /** Данные для интерфейса. У гостя есть только id. */
  function getProfile() {
    if (!player) {
      return { authorized: false, id: null, name: 'Игрок', photo: '' };
    }

    const authorized = player.isAuthorized();

    return {
      authorized,
      id: player.getUniqueID(),
      // Имя может быть пустым, даже если игрок вошёл:
      // он мог запретить передачу персональных данных.
      name: (authorized && player.getName()) || 'Игрок',
      photo: authorized ? player.getPhoto('medium') : '',
    };
  }

  /** Открывает окно Яндекс ID. Возвращает true, если игрок авторизован. */
  async function signIn() {
    if (!ysdk || isAuthorized()) return isAuthorized();

    try {
      await ysdk.auth.openAuthDialog();
    } catch (e) {
      return false; // Отказ пользователя — не ошибка.
    }

    try {
      player = await ysdk.getPlayer();
    } catch (e) {
      console.warn('Не удалось перечитать Player:', e);
      return false;
    }

    return isAuthorized();
  }

  return { init, signIn, isAuthorized, getProfile, getRaw: () => player };
})();

Подключение кнопки. Обратите внимание на две детали, которые проверяет модерация: кнопка скрывается после успешного входа и не блокирует игру, пока идёт диалог.

const signInButton = document.getElementById('sign-in');
const benefitText = document.getElementById('sign-in-benefit');

function renderProfile(profile) {
  document.getElementById('player-name').textContent = profile.name;
  // После входа предложение авторизации больше не показываем (п. 1.2).
  signInButton.hidden = profile.authorized;
  benefitText.hidden = profile.authorized;
}

signInButton.addEventListener('click', async () => {
  signInButton.disabled = true;
  await PlayerAPI.signIn();
  signInButton.disabled = false;
  renderProfile(PlayerAPI.getProfile());
});

(async () => {
  const ysdk = await YaGames.init();
  renderProfile(await PlayerAPI.init(ysdk));
  ysdk.features.LoadingAPI?.ready();
})();

Разметка кнопки с объяснённой выгодой — то самое, чего требует 1.2.1:

<button id="sign-in" type="button">Войти через Яндекс ID</button>
<p id="sign-in-benefit">
  Вход сохранит прогресс в облаке: играйте на телефоне и компьютере
  с одного места и попадайте в таблицу рекордов.
</p>

Данные профиля

МетодВозвращаетДоступен гостю
player.getUniqueID()Постоянный уникальный идентификатор, строкаДа
player.isAuthorized()true / falseДа
player.getName()Имя пользователя, строкаНет
player.getPhoto(size)URL аватара; size'small', 'medium', 'large'Нет
player.getPayingStatus()'paying', 'partially_paying', 'not_paying', 'unknown'Зависит от пользователя
player.getIDsPerGame()Promise<Array<{ appID, userID }>> — идентификаторы игрока в других ваших играхНет, требует авторизации

getPhoto() и getName() синхронные и возвращают значения сразу — отдельного запроса не делают. getIDsPerGame() асинхронный, и перед вызовом документация советует проверить доступность: await ysdk.isAvailableMethod('player.getIDsPerGame').

Осторожно

Метод player.getID() устарел: он пока работает, но пишет предупреждение в консоль. Используйте getUniqueID(). Значения этих двух методов в общем случае не совпадают для одного и того же игрока, хотя у части пользователей могут оказаться одинаковыми. Если ваша игра уже привязывала данные к getID(), переносите их на getUniqueID(); для массовой миграции документация предлагает обратиться в поддержку. Метод player.getMode() тоже помечен устаревшим и будет удалён.

Гостевой прогресс и диалог выбора аккаунта

Требование 1.9 говорит прямо: прогресс сохраняется независимо от того, авторизован пользователь или нет. Технически ничего дополнительного делать не надо — player.setData() и player.setStats() работают у гостя так же.

Интереснее момент перехода. Игрок сначала играл гостем, потом вошёл в аккаунт — и у него оказалось два разных сохранения: одно под логином, другое без него. Платформа сама показывает диалог, где можно сравнить сохранения по длительности игры, дате последнего входа и другим параметрам и выбрать нужное. Ваша задача — не мешать этому и подхватить результат.

ysdk.on(ysdk.EVENTS.ACCOUNT_SELECTION_DIALOG_OPENED, () => {
  // Останавливаем регулярную синхронизацию, пока игрок выбирает.
  stopAutosave();
});

ysdk.on(ysdk.EVENTS.ACCOUNT_SELECTION_DIALOG_CLOSED, async () => {
  // Выходим в главное меню и перечитываем игрока заново.
  goToMainMenu();

  const player = await ysdk.getPlayer();
  const data = await player.getData();

  applySave(data);
  startAutosave();
});

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

player.signature: проверка игрока на сервере

Если у игры есть собственный бэкенд — с серверными сохранениями, античитом или начислением предметов, — нельзя доверять идентификатору, который прислал клиент: его подделает кто угодно. Для этого существует подпись.

const player = await ysdk.getPlayer({ signed: true });

await fetch('https://your.game.server/auth', {
  method: 'POST',
  headers: { 'Content-Type': 'text/plain' },
  body: player.signature,
});

player.signature — это две base64-строки, разделённые точкой: <подпись>.<данные профиля>. Сервер разбирает их, считает HMAC-SHA256 от второй части с секретным ключом игры и сравнивает с первой. Совпало — данным можно верить.

Совет

Секретный ключ уникален для игры и формируется автоматически при создании инап-покупок; он показывается в Консоли разработчика на вкладке Инап-покупки → Настройки. То есть до подключения покупок проверить подпись просто нечем. Если сервера у вас нет, { signed: true } не нужен — обычного getPlayer() достаточно.

Не забудьте, что запросы к вашему серверу из игры — внешние сетевые запросы: домен придётся заранее указать на вкладке CSP в Консоли и обосновать.

Лимиты и типичные ошибки

ysdk.getPlayer() можно вызывать не чаще 20 раз за 5 минут — дальше запрос отклоняется с ошибкой. Звучит щедро, пока кто-то не вызовет getPlayer() внутри игрового цикла или на каждом открытии меню. Правило простое: получить объект один раз при старте, хранить в модуле и перечитывать только в двух случаях — после openAuthDialog() и после закрытия диалога выбора аккаунта.

Что ещё стоит проверить перед отправкой на модерацию:

  • игра полностью проходима без входа в аккаунт, включая сохранение прогресса;
  • окно входа не открывается автоматически при старте, после смерти персонажа или по таймеру;
  • рядом с кнопкой входа написано, что он даёт;
  • у пользователя есть возможность отказаться и продолжить игру;
  • вошедшему игроку предложение авторизации больше не показывается;
  • отказ от авторизации не роняет игру и не оставляет висеть экран загрузки;
  • в игре нет ни одной формы регистрации и ни одной кнопки входа через сторонний сервис.

Отдельно про Claude Code: при генерации кода авторизации модель охотно предлагает { scopes: true } и getID() — это следы старых туториалов в обучающих данных. Если вы просите написать интеграцию, добавляйте в промпт актуальные сигнатуры или держите рядом файл с правилами проекта, чтобы не вычищать легаси вручную после каждой генерации.

Что дальше

Объект Player мы получили — теперь у него есть вторая половина: внутриигровые данные. В следующей главе разберём облачные сохранения: setData и getData для структурированного состояния, setStats и incrementStats для часто меняющихся чисел, параметр flush (именно параметр, а не метод) и лимиты, о которые чаще всего спотыкаются в продакшене.

Источники

Официальная документация платформы — единственный источник истины по её требованиям.

Нужно подробнее?

Полное обучение разбирает те же темы глубже: уроки с примерами, готовые структуры проектов, развёрнутые чек-листы и файл инструкций для Claude Code — он входит в стоимость. Бесплатные главы при этом остаются открытыми и продолжают дополняться.

Что входит в полное обучение