SHiNE-server/Dev_Docs/API/02_Authentication_API.md

13 KiB
Raw Blame History

API для разработчиков: Авторизация

Этот файл описывает именно этапы авторизации клиента, то есть как создать новую сессию и как войти в уже существующую.

Здесь четыре базовых метода обычной авторизации:

  • AuthChallenge
  • CreateAuthSession
  • SessionChallenge
  • SessionLogin

Логика раздела такая:

  • сначала клиент либо начинает создание новой сессии через deviceKey;
  • либо начинает вход в уже созданную сессию через 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

deviceKey используется для создания новой сессии.

sessionKey используется для входа в уже созданную сессию.

sessionKey передаётся и хранится целиком одной строкой, например:

ed25519/BASE64_PUBLIC_KEY

2. AuthChallenge

Запрос

{
  "op": "AuthChallenge",
  "requestId": "auth-001",
  "payload": {
    "login": "alice"
  }
}

Успешный ответ

{
  "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

Запрос

{
  "op": "CreateAuthSession",
  "requestId": "create-001",
  "payload": {
    "login": "alice",
    "sessionKey": "ed25519/BASE64_PUBLIC_KEY",
    "storagePwd": "BASE64_OR_APP_SPECIFIC_SECRET",
    "timeMs": 1774600000123,
    "authNonce": "nonce",
    "deviceKey": "BASE64_DEVICE_PUBLIC_KEY",
    "signatureB64": "BASE64_SIGNATURE",
    "sessionType": 1,
    "clientPlatform": "Web",
    "clientInfo": "Android 15; Pixel 9"
  }
}

Строка для подписи

AUTH_CREATE_SESSION:{login}:{sessionKey}:{storagePwd}:{timeMs}:{authNonce}

Дополнительная проверка ключа

Перед проверкой подписи сервер должен:

  1. взять актуальный solana_users.device_key;
  2. сравнить его с payload.deviceKey;
  3. только потом проверять подпись.

Если ключ не совпадает, сервер возвращает ошибку DEVICE_KEY_NOT_ACTUAL.

На будущее:

  • для ротации device_key желательно добавить перепроверку через Solana.

Успешный ответ

{
  "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_MISMATCHlogin не совпадает с тем, для кого был выдан 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 или deviceKey не поддерживается текущим сервером.
  • 400 / BAD_BASE64 — неверный Base64 в sessionKey, deviceKey или signatureB64.
  • 400 / EMPTY_SIGNATURE — пустая подпись.
  • 400 / TIME_SKEW — время клиента отличается от серверного больше допустимого окна.
  • 400 / NO_DEVICE_KEYу пользователя в БД отсутствует deviceKey.
  • 400 / EMPTY_AUTH_NONCE — пустой authNonce.
  • 400 / AUTH_NONCE_MISMATCHauthNonce не соответствует значению из AuthChallenge.
  • 400 / EMPTY_DEVICE_KEY — в запросе не передан deviceKey.
  • 422 / DEVICE_KEY_NOT_ACTUALdeviceKey не совпадает с актуальной версией на сервере.
  • 422 / BAD_SIGNATURE — подпись не прошла проверку.
  • 460 / SESSION_TYPE_MISMATCHsessionType не совпадает с типом сессии, уже опубликованным для этого sessionKey в Solana PDA.
  • 501 / SESSION_TYPE_PDA_CHECK_FAILED — сервер не смог проверить sessionType по Solana PDA.
  • 501 / DB_ERROR_SESSION_CREATE — ошибка БД при создании записи активной сессии.
  • 500 / INTERNAL_ERROR — непредвиденная внутренняя ошибка сервера.

4. SessionChallenge

Запрос

{
  "op": "SessionChallenge",
  "requestId": "sch-001",
  "payload": {
    "sessionId": "sess_7c5e5c4b"
  }
}

Успешный ответ

{
  "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

Запрос

{
  "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"
  }
}

Строка для подписи

SESSION_LOGIN:{sessionId}:{timeMs}:{nonce}

Дополнительная проверка ключа

Перед проверкой подписи сервер должен:

  1. взять active_sessions.session_key;
  2. сравнить его с payload.sessionKey;
  3. только потом проверять подпись.

Если ключ не совпадает, сервер возвращает ошибку SESSION_KEY_NOT_ACTUAL.

Успешный ответ

{
  "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_MISMATCHsessionType не совпадает с типом сессии, уже опубликованным для этого 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, относящиеся к этому сценарию:

  • UpsertEspPairingSettings
  • StartEspPairing
  • ListEspPairingRequests
  • ApproveEspPairing
  • RejectEspPairing
  • GetEspPairingStatus

В этом потоке:

  • новое устройство не владеет deviceKey и не проходит обычный CreateAuthSession;
  • пароль проверяется сервером только как фильтр;
  • решение об одобрении принимает уже авторизованная доверенная сессия пользователя;
  • сервер не расшифровывает encryptedPayload и не становится источником приватных ключей.

Точные форматы этих операций см. в 03_Session_Management_API.md и в протокольном документе:

  • Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md

6. Пример ошибки

{
  "op": "SessionLogin",
  "requestId": "slogin-001",
  "status": 403,
  "ok": false,
  "error": "SESSION_KEY_NOT_ACTUAL",
  "message": "session_key не соответствует актуальной версии",
  "payload": {
  }
}