Files
2026-09-03 09:16:56 +03:00

11 KiB

AuthService

Обзор класса

Minecraft_launcher поддерживает три способа входа: офлайн-профиль без пароля, учётную запись Ely.by и учётную запись Microsoft. AuthService закрывает первые два: это Yggdrasil-клиент Ely.by плюс офлайн-режим. За третий отвечает MsaAuthService.

Результат любого способа — структура AuthResult, объявленная в этом же заголовке. Она содержит ровно то, что подставляется в аргументы запуска вида ${auth_*}, поэтому дальше запуск игры идёт по общему пути независимо от того, как пользователь вошёл.

Класс также умеет скачивать authlib-injector — библиотеку, которая перенаправляет обращения игры к серверу авторизации на Ely.by.

Место в проекте и зависимости

Экземпляр создаётся и принадлежит LauncherBackend; других владельцев нет. Заголовок подключает MsaAuthService — ради общей структуры AuthResult — и GameLauncher через поля LaunchOptions, которые заполняются из результата авторизации.

Требования сборки: Qt6::Core (QDateTime, QJsonObject, QString) и Qt6::Network (QNetworkAccessManager).

Иерархия и роль

Наследует QObject: даёт мета-объектную систему, сигнал progress и владение по родителю. Виртуальных методов базового класса не переопределяет.

Публичные структуры

AuthResult

Результат авторизации — то, что подставляется в ${auth_*} аргументы запуска.

Поле Тип Описание
ok bool Авторизация удалась
twoFactorRequired bool Ely.by отклонил пароль с пометкой two factor — нужен одноразовый код
licenseMissing bool Вход в Microsoft прошёл, но копии игры на аккаунте нет. Обрабатывается отдельно от прочих ошибок, потому что чинится только покупкой
error QString Текст ошибки, когда ok равен false
playerName QString Подставляется в ${auth_player_name}
uuid QString Подставляется в ${auth_uuid}; hex без дефисов
accessToken QString Подставляется в ${auth_access_token}
clientToken QString Подставляется в ${clientid}
userType QString Подставляется в ${user_type}; принимает значения legacy (офлайн), msa (Microsoft) и ELYBY
refreshToken QString Только для аккаунтов Microsoft: продлевает сессию без ввода пароля
xuid QString Только для Microsoft; подставляется в ${auth_xuid}
expiresAt QDateTime Только для Microsoft: UTC-время, когда протухает accessToken

Псевдонимы типов

AuthService::Callbackstd::function<void(const AuthResult &)>. Все сетевые методы асинхронные: колбэк вызывается ровно один раз и всегда в потоке GUI.

Публичные методы

explicit AuthService(QObject *parent = nullptr)

Создаёт сервис и его QNetworkAccessManager. Конструктор помечен explicit.

static AuthResult offline(const QString &nickname)

Готовит результат для офлайн-профиля без единого сетевого запроса. UUID выводится из ника так же, как это делает сам Minecraft в офлайне: UUID.nameUUIDFromBytes(("OfflinePlayer:" + name) .getBytes(UTF_8)). Благодаря этому один и тот же ник всегда даёт один и тот же UUID, и прогресс на сервере не теряется.

static QString generateClientToken()

Случайный clientToken лаунчера. Генерируется один раз на профиль и хранится вместе с ним: Yggdrasil связывает выданный accessToken именно с этим значением.

void loginElyBy(const QString &login, const QString &password, const QString &clientToken, const QString &accessToken, Callback callback)

Полный цикл входа в Ely.by: сначала проверка имеющегося токена, затем его продление, и только при неудаче — авторизация по паролю. Пароль можно оставить пустым, если уже есть рабочий accessToken, — тогда пользователю не придётся вводить его заново.

По ходу работы испускает progress с описанием текущего шага. Результат приходит в callback один раз; при ответе с пометкой двухфакторной аутентификации в нём выставлен twoFactorRequired, и вызывающий код должен спросить у пользователя код и продолжить через loginElyByWithTotp().

void loginElyByWithTotp(const QString &login, const QString &password, const QString &totp, const QString &clientToken, Callback callback)

Повтор авторизации с одноразовым кодом двухфакторной аутентификации. Пароль и код объединяются в одно поле в формате «пароль:код», как того требует Ely.by.

void ensureAuthlibInjector(const QString &targetDir, std::function<void(const QString &path, const QString &error)> callback)

Скачивает authlib-injector в targetDir, если его там ещё нет. Колбэк получает либо путь к готовому jar, либо текст ошибки — заполнено всегда ровно одно из двух.

Библиотека нужна только для профилей Ely.by: она подключается к JVM аргументом -javaagent и перенаправляет обращения игры к серверу авторизации.

Сигналы

progress(const QString &message)

Описание текущего шага авторизации. Испускается по ходу всех сетевых операций.

Обработчик показывает сообщение пользователю: в главном окне лаунчера оно попадает в плашку статуса и держится до следующего сообщения, потому что шаг может занять заметное время.

Владение и время жизни

Класс наследует QObject и принимает parent в конструкторе — родитель его и удалит. QNetworkAccessManager создаётся в конструкторе с сервисом в роли родителя и уничтожается вместе с ним.

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

Потокобезопасность

Только поток GUI. Все сетевые методы асинхронные, и их колбэки вызываются в том же потоке, в котором создан сервис. Собственной синхронизации в классе нет.

Взаимодействие с другими классами

LauncherBackend вызывает методы входа при запуске игры и переправляет сигнал progress в интерфейс. Полученный AuthResult он раскладывает по полям LaunchOptions, которые уходят в GameLauncher. Путь, возвращённый ensureAuthlibInjector(), попадает в поле authlibInjectorPath тех же параметров запуска.

Сохранением токенов между запусками занимается LauncherBackend: сам сервис ничего не пишет на диск, кроме скачанного jar.

Внешнее взаимодействие

Сеть, исходящие запросы. Класс общается с сервером авторизации Ely.by через QNetworkAccessManager. Формат — JSON поверх HTTPS, запросы инициирует всегда лаунчер. Внутренний помощник postJson() разделяет три исхода: успешный ответ, ответ с кодом ошибки и транспортную ошибку — последняя отдаётся отдельным параметром, чтобы отличить недоступную сеть от отказа сервера.

Отдельным каналом идёт загрузка authlib-injector — обычная HTTPS-загрузка файла в targetDir. Повторных попыток при неудаче класс не делает: решение о повторе принимает вызывающий код.

Все сигналы и колбэки приходят в поток GUI.

Пример использования

auto *auth = new AuthService(this);
connect(auth, &AuthService::progress, this, &Backend::showStatus);

auth->loginElyBy(profile.login, profile.password,
                 profile.clientToken, profile.accessToken,
                 [this](const AuthResult &result) {
                     if (result.twoFactorRequired) {
                         emit twoFactorRequired(m_pendingProfileName);
                         return;
                     }
                     if (!result.ok) {
                         emit launchError(result.error);
                         return;
                     }
                     continueLaunch(result);
                 });

При создании этого документа использовался ИИ.