82 lines
5.9 KiB
Markdown
82 lines
5.9 KiB
Markdown
# 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-потоке. Шаги вызываются из колбэков предыдущих. Если удалить
|
||
сервис посреди цепочки, она оборвётся вместе с менеджером сети, и колбэк не вызовется.
|
||
|
||
---
|
||
|
||
При создании этого документа использовался ИИ.
|