# API для разработчиков: Авторизация Этот файл описывает именно этапы авторизации клиента, то есть как создать новую сессию и как войти в уже существующую. Здесь четыре базовых метода обычной авторизации: - `AuthChallenge` - `CreateAuthSession` - `SessionChallenge` - `SessionLogin` Логика раздела такая: - сначала клиент либо начинает создание новой сессии через `clientKey`; - либо начинает вход в уже созданную сессию через `sessionKey`; - сервер на первом шаге выдаёт challenge/nonce; - на втором шаге клиент присылает подписанный ответ; - сервер сверяет актуальные публичные ключи и только потом проверяет подпись. Новые поля этого раздела: - `sessionType` — числовой код типа сессии; - `clientPlatform` — свободная строка платформы клиента. Текущие поддерживаемые коды `sessionType`: - `1` — обычный клиент; - `50` — кошелёк; - `100` — homeserver. Правило проверки `sessionType`: 1. если в `Solana PDA` нет записи для `sessionKey`, сервер принимает `sessionType`, присланный клиентом; 2. если запись в `PDA` есть, `sessionType` в запросе должен совпадать с `session_type` из `PDA`; 3. при несовпадении сервер возвращает `460 / SESSION_TYPE_MISMATCH`. Ниже в документе сначала описан сценарий, а потом зафиксированы точные форматы запросов и ответов. Отдельно появился новый серверный сценарий pairing через доверенный homeserver/ESP. Он не заменяет обычный вход и описан в: - `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md` Кратко: - `AuthChallenge/CreateAuthSession` и `SessionChallenge/SessionLogin` остаются каноническими потоками обычной авторизации; - pairing через ESP идёт отдельными `op` и только подготавливает безопасное добавление новой сессии; - решение об одобрении pairing принимает любая уже авторизованная доверенная сессия пользователя. ## 1. Поток авторизации Поддерживаются два сценария: 1. Создание новой сессии: `AuthChallenge` -> `CreateAuthSession` 2. Вход в существующую сессию: `SessionChallenge` -> `SessionLogin` `clientKey` используется для создания новой сессии. `sessionKey` используется для входа в уже созданную сессию. `sessionKey` передаётся и хранится целиком одной строкой, например: ```text ed25519/BASE64_PUBLIC_KEY ``` --- ## 2. `AuthChallenge` ### Запрос ```json { "op": "AuthChallenge", "requestId": "auth-001", "payload": { "login": "alice" } } ``` ### Успешный ответ ```json { "op": "AuthChallenge", "requestId": "auth-001", "status": 200, "ok": true, "payload": { "authNonce": "8f2f0f71-0b1c-4ab2-8f5d-0bc5d6f6aa11" } } ``` ### Специфические коды ошибок `AuthChallenge` - `400 / EMPTY_LOGIN` — пустой `login`. - `400 / ALREADY_AUTHED` — по текущему соединению уже выполнена авторизация. - `422 / UNKNOWN_USER` — пользователь с таким `login` не найден. - `501 / SOLANA_IMPORT_FAILED` — сервер не смог проверить/импортировать пользователя из Solana при lazy-import. - `500 / INTERNAL_ERROR` — непредвиденная внутренняя ошибка сервера, если появится вне штатного сценария. --- ## 3. `CreateAuthSession` ### Запрос ```json { "op": "CreateAuthSession", "requestId": "create-001", "payload": { "login": "alice", "sessionKey": "ed25519/BASE64_PUBLIC_KEY", "storagePwd": "BASE64_OR_APP_SPECIFIC_SECRET", "timeMs": 1774600000123, "authNonce": "nonce", "clientKey": "BASE64_DEVICE_PUBLIC_KEY", "signatureB64": "BASE64_SIGNATURE", "sessionType": 1, "clientPlatform": "Web", "clientInfo": "Android 15; Pixel 9" } } ``` ### Строка для подписи ```text AUTH_CREATE_SESSION:{login}:{sessionKey}:{storagePwd}:{timeMs}:{authNonce} ``` ### Дополнительная проверка ключа Перед проверкой подписи сервер должен: 1. взять актуальный `solana_users.client_key`; 2. сравнить его с `payload.clientKey`; 3. только потом проверять подпись. Если `clientKey` не совпадает, сервер возвращает ошибку `DEVICE_KEY_NOT_ACTUAL`. На будущее: - для ротации `client_key` желательно добавить перепроверку через Solana. ### Успешный ответ ```json { "op": "CreateAuthSession", "requestId": "create-001", "status": 200, "ok": true, "payload": { "sessionId": "sess_7c5e5c4b" } } ``` ### Специфические коды ошибок `CreateAuthSession` - `400 / NO_STEP1_CONTEXT` — для данного соединения не был корректно выполнен `AuthChallenge`. - `400 / EMPTY_LOGIN` — пустой `login`. - `400 / LOGIN_MISMATCH` — `login` не совпадает с тем, для кого был выдан `authNonce`. - `501 / DB_ERROR_USER_LOOKUP` — ошибка БД при повторном чтении пользователя. - `422 / USER_NOT_FOUND` — пользователь не найден. - `501 / NO_LOGIN` — у пользователя на сервере не заполнен `login`. - `400 / EMPTY_STORAGE_PWD` — пустой `storagePwd`. - `400 / EMPTY_SESSION_KEY` — пустой `sessionKey`. - `422 / UNSUPPORTED_KEY_ALGORITHM` — префикс алгоритма в `sessionKey` или `clientKey` не поддерживается текущим сервером. - `400 / BAD_BASE64` — неверный Base64 в `sessionKey`, `clientKey` или `signatureB64`. - `400 / EMPTY_SIGNATURE` — пустая подпись. - `400 / TIME_SKEW` — время клиента отличается от серверного больше допустимого окна. - `400 / NO_DEVICE_KEY` — у пользователя в БД отсутствует `clientKey`. - `400 / EMPTY_AUTH_NONCE` — пустой `authNonce`. - `400 / AUTH_NONCE_MISMATCH` — `authNonce` не соответствует значению из `AuthChallenge`. - `400 / EMPTY_DEVICE_KEY` — в запросе не передан `clientKey`. - `422 / DEVICE_KEY_NOT_ACTUAL` — `clientKey` не совпадает с актуальной версией на сервере. - `422 / BAD_SIGNATURE` — подпись не прошла проверку. - `460 / SESSION_TYPE_MISMATCH` — `sessionType` не совпадает с типом сессии, уже опубликованным для этого `sessionKey` в Solana PDA. - `501 / SESSION_TYPE_PDA_CHECK_FAILED` — сервер не смог проверить `sessionType` по Solana PDA. - `501 / DB_ERROR_SESSION_CREATE` — ошибка БД при создании записи активной сессии. - `500 / INTERNAL_ERROR` — непредвиденная внутренняя ошибка сервера. --- ## 4. `SessionChallenge` ### Запрос ```json { "op": "SessionChallenge", "requestId": "sch-001", "payload": { "sessionId": "sess_7c5e5c4b" } } ``` ### Успешный ответ ```json { "op": "SessionChallenge", "requestId": "sch-001", "status": 200, "ok": true, "payload": { "nonce": "0e5bb0f4-c7d8-4efb-b44d-bf31a6126c66" } } ``` ### Специфические коды ошибок `SessionChallenge` - `400 / EMPTY_SESSION_ID` — пустой `sessionId`. - `501 / DB_ERROR` — ошибка БД при чтении сессии. - `422 / SESSION_NOT_FOUND` — сессия не найдена. - `500 / INTERNAL_ERROR` — непредвиденная внутренняя ошибка сервера. --- ## 5. `SessionLogin` ### Запрос ```json { "op": "SessionLogin", "requestId": "slogin-001", "payload": { "sessionId": "sess_7c5e5c4b", "sessionKey": "ed25519/BASE64_PUBLIC_KEY", "timeMs": 1774600010456, "signatureB64": "BASE64_SIGNATURE", "sessionType": 1, "clientPlatform": "Web", "clientInfo": "Android 15; Pixel 9" } } ``` ### Строка для подписи ```text SESSION_LOGIN:{sessionId}:{timeMs}:{nonce} ``` ### Дополнительная проверка ключа Перед проверкой подписи сервер должен: 1. взять `active_sessions.session_key`; 2. сравнить его с `payload.sessionKey`; 3. только потом проверять подпись. Если ключ не совпадает, сервер возвращает ошибку `SESSION_KEY_NOT_ACTUAL`. ### Успешный ответ ```json { "op": "SessionLogin", "requestId": "slogin-001", "status": 200, "ok": true, "payload": { "storagePwd": "BASE64_OR_APP_SPECIFIC_SECRET" } } ``` ### Специфические коды ошибок `SessionLogin` - `400 / EMPTY_SESSION_ID` — пустой `sessionId`. - `400 / NO_CHALLENGE` — перед `SessionLogin` не был успешно выполнен `SessionChallenge` либо nonce уже истёк. - `400 / SESSION_ID_MISMATCH` — nonce был выдан для другого `sessionId`. - `400 / TIME_SKEW` — время клиента отличается от серверного больше допустимого окна. - `400 / EMPTY_SIGNATURE` — пустая подпись. - `400 / EMPTY_SESSION_KEY` — пустой `sessionKey`. - `501 / DB_ERROR` — ошибка БД при чтении сессии. - `422 / SESSION_NOT_FOUND` — сессия не найдена. - `501 / NO_SESSION_KEY` — у сессии отсутствует `session_key`. - `422 / SESSION_KEY_NOT_ACTUAL` — переданный `sessionKey` не совпадает с актуальной версией на сервере. - `422 / UNSUPPORTED_KEY_ALGORITHM` — префикс алгоритма в `sessionKey` не поддерживается текущим сервером. - `400 / BAD_BASE64` — неверный Base64 в `sessionKey` или `signatureB64`. - `422 / BAD_SIGNATURE` — подпись не прошла проверку. - `460 / SESSION_TYPE_MISMATCH` — `sessionType` не совпадает с типом сессии, уже опубликованным для этого `sessionKey` в Solana PDA. - `501 / SESSION_TYPE_PDA_CHECK_FAILED` — сервер не смог проверить `sessionType` по Solana PDA. - `501 / DB_ERROR_USER_LOOKUP` — ошибка БД при чтении пользователя для этой сессии. - `422 / USER_NOT_FOUND_FOR_SESSION` — пользователь, которому принадлежит сессия, не найден. - `500 / INTERNAL_ERROR` — непредвиденная внутренняя ошибка сервера. --- ## 6. Pairing через homeserver/ESP Новые `op`, относящиеся к этому сценарию: - `GetTrustedDeviceLoginSettings` - `UpsertTrustedDeviceLoginSettings` - `StartTrustedDeviceLogin` - `ListTrustedDeviceLoginRequests` - `ApproveTrustedDeviceLogin` - `RejectTrustedDeviceLogin` - `CancelTrustedDeviceLogin` - `GetTrustedDeviceLoginStatus` В этом потоке: - новое устройство не владеет `clientKey` и не проходит обычный `CreateAuthSession`; - пароль проверяется сервером только как фильтр; - решение об одобрении принимает уже авторизованная доверенная сессия пользователя; - сервер не расшифровывает `encryptedPayload` и не становится источником приватных ключей. Точные форматы этих операций см. в `03_Session_Management_API.md` и в протокольном документе: - `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md` --- ## 6. Пример ошибки ```json { "op": "SessionLogin", "requestId": "slogin-001", "status": 403, "ok": false, "error": "SESSION_KEY_NOT_ACTUAL", "message": "session_key не соответствует актуальной версии", "payload": { } } ```