Files
BiLauncher/doc/cpp/MsaAuthService.md
T
2026-10-11 22:32:13 +03:00

82 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MsaAuthService
`msaauthservice.h`, `msaauthservice.cpp`
Вход через аккаунт Microsoft, то есть с лицензионной копией игры. Цепочка такая же, как у
официального лаунчера: OAuth2 → Xbox Live → XSTS → Minecraft Services → проверка лицензии →
профиль. Результат приходит в `AuthResult` из [AuthService](AuthService.md).
Окно входа сервис не показывает. Страницу Microsoft открывает
[MicrosoftLoginDialog](../qml/MicrosoftLoginDialog.md), а сервис даёт адрес этой страницы и умеет
узнать адрес возврата. Класс компилируется всегда, но без Qt WebEngine показать страницу нечем, и
[LauncherBackend](LauncherBackend.md) сразу отвечает ошибкой.
## Чьё это приложение
Сейчас используется `client_id` официального лаунчера Mojang (`00000000402b5328`). Это старое
приложение Live Connect, поэтому и адреса от `login.live.com`:
| Константа | Значение |
|-----------|----------|
| `kAuthorizeUrl` | `https://login.live.com/oauth20_authorize.srf` |
| `kTokenUrl` | `https://login.live.com/oauth20_token.srf` |
| `kRedirectUri` | `https://login.live.com/oauth20_desktop.srf` |
| `kScope` | `service::user.auth.xboxlive.com::MBI_SSL` |
| `kRpsTicketPrefix` | пусто |
Это временное решение. Чтобы перейти на своё приложение в Azure (с включённым «Allow public client
flows»), меняются эти шесть констант. Что именно подставить, написано в комментарии в начале
`msaauthservice.cpp`. Главное, что легко пропустить: у нового приложения в `RpsTicket` нужна
приставка `d=`.
`clientId()` отдаёт этот id наружу. Бэкенд подставляет его в `${clientid}`, потому что официальный
лаунчер передаёт туда id приложения, а не случайный токен.
## Окно входа
`authorizationUrl()` — адрес страницы входа с `client_id`, `response_type=code`, `redirect_uri`,
`scope` и `prompt=select_account`. Последний параметр нужен, чтобы при повторном входе можно было
выбрать другой аккаунт.
`matchRedirect(url, &code, &error)` возвращает `true`, только если хост и путь совпадают с
`kRedirectUri`. Код или ошибка ищутся сначала в query, потом во фрагменте. В `error` попадает
`error_description`, а если его нет, то `error`. Эта проверка сделана в C++, а не в QML, чтобы она
всегда совпадала с тем адресом, который сервис сам передаёт в `redirect_uri`.
## Цепочка
`loginWithCode(code, callback)` меняет код на токен (`grant_type=authorization_code`).
`loginWithRefreshToken(refreshToken, callback)` делает то же по refresh-токену. Дальше путь общий:
1. **Токен Microsoft.** Форма на `kTokenUrl`. Из ответа берутся `access_token` и `refresh_token`.
2. **Xbox Live.** `user.auth.xboxlive.com/user/authenticate`, `RpsTicket` = приставка + токен
Microsoft.
3. **XSTS.** `xsts.auth.xboxlive.com/xsts/authorize` для `rp://api.minecraftservices.com/`. При 401
код `XErr` переводится в понятный текст: нет профиля Xbox, страна не поддерживается, нужна
проверка возраста, детский аккаунт без разрешения взрослого. Отсюда же берутся `uhs` и `xid`
(он станет `xuid`).
4. **Minecraft Services.** `login_with_xbox` с `XBL3.0 x=<uhs>;<xsts>`. В ответе токен Minecraft и
`expires_in`, обычно сутки; от него считается `expiresAt`.
5. **Лицензия.** `entitlements/mcstore`. Результат только запоминается и сам вход не останавливает.
6. **Профиль.** `minecraft/profile`. Ответы 404 и 401 означают, что профиля нет: выставляется
`licenseMissing`. Текст ошибки зависит от шага 5. Если игра куплена, значит, игрок ещё не выбрал
ник. Если не куплена, значит, лицензии нет.
`userType` = `msa`, `clientToken` = id приложения.
Каждый шаг испускает `progress(message)` до отправки запроса. Таймаут на каждый запрос 20 секунд,
повторных попыток нет. Сетевая ошибка и отказ сервера дают разные тексты.
Refresh-токен Microsoft живёт намного дольше суточного токена Minecraft, поэтому при обычном
запуске окно входа не нужно: бэкенд вызывает `loginWithRefreshToken()`. Если Microsoft не вернул
новый refresh-токен, бэкенд оставляет старый.
## Колбэки
Колбэк вызывается один раз в GUI-потоке. Шаги вызываются из колбэков предыдущих. Если удалить
сервис посреди цепочки, она оборвётся вместе с менеджером сети, и колбэк не вызовется.
---
При создании этого документа использовался ИИ.