14 KiB
API для разработчиков: Управление сессиями
Этот файл описывает методы, которые используются уже после успешной авторизации пользователя в сессию.
Здесь два метода:
ListSessions— получить список активных сессий пользователя;CloseActiveSession— закрыть одну из активных сессий.
Дополнительно в этом же слое управления сессиями появился сценарий pairing через доверенную уже авторизованную сессию пользователя:
GetTrustedDeviceLoginSettingsUpsertTrustedDeviceLoginSettingsListTrustedDeviceLoginRequestsApproveTrustedDeviceLoginRejectTrustedDeviceLoginCancelTrustedDeviceLogin
Анонимное новое устройство работает с двумя связанными операциями:
StartTrustedDeviceLoginGetTrustedDeviceLoginStatus
Логика раздела такая:
- сначала пользователь проходит
SessionLogin; - после этого сервер считает соединение авторизованным;
- уже в этом состоянии клиент может читать список сессий и управлять ими.
То есть это не этап создания или входа в сессию, а этап последующего контроля уже существующих активных сессий.
1. ListSessions
Доступно только после успешного SessionLogin.
Запрос
{
"op": "ListSessions",
"requestId": "list-001",
"payload": {
}
}
Успешный ответ
{
"op": "ListSessions",
"requestId": "list-001",
"status": 200,
"ok": true,
"payload": {
"sessions": [
{
"sessionId": "sess_7c5e5c4b",
"sessionType": 1,
"clientPlatform": "Web",
"onlineOnThisServer": true,
"clientInfoFromClient": "Android 15; Pixel 9",
"clientInfoFromRequest": "UA=Java-http-client/17.0.18; remote=127.0.0.1",
"geo": "RU/Moscow",
"lastAuthenticatedAtMs": 1774600010500
}
]
}
}
Специфические коды ошибок ListSessions
422 / NOT_AUTHENTICATED— запрос доступен только после успешногоSessionLogin.501 / DB_ERROR_LIST_SESSIONS— ошибка БД при чтении списка активных сессий.500 / INTERNAL_ERROR— непредвиденная внутренняя ошибка сервера.
Поля одной сессии в ListSessions
sessionId— идентификатор активной сессии;sessionType— числовой код типа сессии:1— клиент;50— кошелёк;100— homeserver;
clientPlatform— строка платформы, как её прислал клиент;onlineOnThisServer—true, если эта сессия сейчас держит живое WebSocket-подключение именно к данному серверу;clientInfoFromClient— краткая строка клиента;clientInfoFromRequest— строка, собранная сервером из запроса;geo— страна/город или fallback-строка;lastAuthenticatedAtMs— время последней успешной авторизации этой сессии.
2. CloseActiveSession
Доступно только после успешного SessionLogin.
Запрос
{
"op": "CloseActiveSession",
"requestId": "close-001",
"payload": {
"sessionId": "sess_7c5e5c4b"
}
}
Успешный ответ
{
"op": "CloseActiveSession",
"requestId": "close-001",
"status": 200,
"ok": true,
"payload": {
}
}
Специфические коды ошибок CloseActiveSession
422 / NOT_AUTHENTICATED— запрос доступен только после успешногоSessionLogin.400 / NO_SESSION_TO_CLOSE— сервер не смог определить, какую сессию нужно закрыть.501 / DB_ERROR— ошибка БД при поиске сессии или её удалении.422 / SESSION_NOT_FOUND— целевая сессия не найдена.422 / SESSION_OF_ANOTHER_USER— нельзя закрывать сессию другого пользователя.500 / INTERNAL_ERROR— непредвиденная внутренняя ошибка сервера.
3. Пример ошибки
{
"op": "CloseActiveSession",
"requestId": "close-001",
"status": 403,
"ok": false,
"error": "NOT_AUTHENTICATED",
"message": "Операция доступна только для авторизованных пользователей",
"payload": {
}
}
4. Формат sessionId
Текущее серверное значение sessionId генерируется как:
- случайные 32 байта (
SecureRandom), - кодирование в стандартный Base64 RFC 4648 (алфавит
A-Z a-z 0-9 + /), - без padding
=.
Практически это строка длиной около 43 символов (для 32 байт без =).
Пример реального формата:
K9v3nQ4u8jYk0a2p7cD4mLx1zR0sT5wV6bN8eH3fQ1M
Важно: это не человеко-читаемое имя, а непрозрачный идентификатор. Нужно передавать его как есть, без нормализации регистра и без URL-экранирования внутри JSON.
5. TrustedDeviceLogin через доверенную сессию
Этот блок относится к сценарию добавления новой сессии через доверенное устройство пользователя.
5.1. GetTrustedDeviceLoginSettings
Доступно для любой уже авторизованной доверенной сессии пользователя.
Запрос
{
"op": "GetTrustedDeviceLoginSettings",
"requestId": "trusted-login-get-001",
"payload": {
}
}
Успешный ответ
{
"op": "GetTrustedDeviceLoginSettings",
"requestId": "trusted-login-get-001",
"status": 200,
"ok": true,
"payload": {
"enabled": true,
"hasPassword": false
}
}
Если отдельной записи настроек на сервере ещё нет, сервер считает состояние по умолчанию таким:
enabled = truehasPassword = false
Ошибки
463 / PAIRING_REQUIRES_AUTH_SESSION— операция вызвана без уже авторизованной доверенной сессии пользователя.
5.2. UpsertTrustedDeviceLoginSettings
Доступно для любой уже авторизованной доверенной сессии пользователя.
Запрос
{
"op": "UpsertTrustedDeviceLoginSettings",
"requestId": "esp-set-001",
"payload": {
"enabled": true,
"passwordHash": "sha256$0123abcd..."
}
}
Если вход через доверенное устройство должен работать без доп. пароля, клиент включает его с пустым passwordHash.
Если enabled = false, сервер автоматически удаляет пароль и запрещает вход через другое устройство.
Формат непустого passwordHash:
sha256$<hex( SHA-256("shine-pairing|" + lower(login.trim()) + "|" + password) )>
Успешный ответ
{
"op": "UpsertTrustedDeviceLoginSettings",
"requestId": "esp-set-001",
"status": 200,
"ok": true,
"payload": {
"enabled": true,
"hasPassword": true
}
}
Ошибки
463 / PAIRING_REQUIRES_AUTH_SESSION— операция вызвана без уже авторизованной доверенной сессии пользователя.
5.3. StartTrustedDeviceLogin
Эта операция доступна без уже существующей пользовательской сессии.
Запрос
{
"op": "StartTrustedDeviceLogin",
"requestId": "esp-start-001",
"payload": {
"login": "alice",
"passwordHash": "sha256$0123abcd...",
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY",
"requesterSessionType": 1,
"requesterClientPlatform": "Android",
"payloadType": 1
}
}
Если на доверённом устройстве вход включён без доп. пароля, новое устройство может отправить пустой passwordHash.
Поле trustedSessionOnline показывает, что у пользователя сейчас есть хотя бы одна онлайн доверенная сессия, способная принять pairing-заявку.
Поле shortCode теперь содержит 10 цифр. В UI его рекомендуется показывать как 5 пар, например: 49 20 70 91 23.
TTL заявки фиксирован на сервере и сейчас всегда равен 300 секундам.
Успешный ответ
{
"op": "StartTrustedDeviceLogin",
"requestId": "esp-start-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "created",
"shortCode": "4920709123",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"expiresAtMs": 1781441990538,
"trustedSessionOnline": true
}
}
Ошибки
400 / EMPTY_LOGIN400 / EMPTY_REQUESTER_SESSION_KEY400 / BAD_REQUESTER_SESSION_KEY400 / BAD_SESSION_TYPE400 / BAD_PAYLOAD_TYPE422 / PAIRING_NOT_AVAILABLE422 / PAIRING_PASSWORD_INVALID— pairing-пароль не подходит. Та же ошибка возвращается и если новое устройство ввело пароль, а у пользователя режим pairing включён без пароля.422 / PAIRING_NO_TRUSTED_SESSION_ONLINE— сейчас нет ни одной онлайн доверённой сессии пользователя, поэтому код не создаётся.429 / PAIRING_RATE_LIMITED
5.4. ListTrustedDeviceLoginRequests
Доступно для любой уже авторизованной доверенной сессии пользователя.
Возвращает только реально активные pending-заявки со state = created. Уже approved и rejected заявки в этот список больше не попадают.
Успешный ответ
{
"op": "ListTrustedDeviceLoginRequests",
"requestId": "esp-list-001",
"status": 200,
"ok": true,
"payload": {
"requests": [
{
"pairingId": "base64url",
"state": "created",
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY",
"requesterSessionType": 1,
"requesterClientPlatform": "Android",
"payloadType": 1,
"shortCode": "4920709123",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"createdAtMs": 1781441810538,
"expiresAtMs": 1781441990538,
"deliveredToHomeserver": true
}
]
}
}
Ошибки
463 / PAIRING_REQUIRES_AUTH_SESSION
5.5. ApproveTrustedDeviceLogin
Доступно для любой уже авторизованной доверенной сессии пользователя.
Запрос
{
"op": "ApproveTrustedDeviceLogin",
"requestId": "esp-approve-001",
"payload": {
"pairingId": "base64url",
"encryptedPayload": "BASE64_OR_OTHER_OPAQUE_PAYLOAD"
}
}
Успешный ответ
{
"op": "ApproveTrustedDeviceLogin",
"requestId": "esp-approve-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "approved"
}
}
Ошибки
400 / EMPTY_PAIRING_ID400 / EMPTY_ENCRYPTED_PAYLOAD404 / PAIRING_NOT_FOUND422 / PAIRING_OF_ANOTHER_USER422 / PAIRING_NOT_PENDING422 / PAIRING_EXPIRED463 / PAIRING_REQUIRES_AUTH_SESSION
5.6. RejectTrustedDeviceLogin
Доступно для любой уже авторизованной доверенной сессии пользователя. Похоже на approve, но переводит заявку в state=rejected.
5.7. GetTrustedDeviceLoginStatus
Операция для нового устройства.
Запрос
{
"op": "GetTrustedDeviceLoginStatus",
"requestId": "esp-status-001",
"payload": {
"pairingId": "base64url"
}
}
Успешный ответ после approve
{
"op": "GetTrustedDeviceLoginStatus",
"requestId": "esp-status-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "approved",
"shortCode": "4920709123",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"payloadType": 1,
"encryptedPayload": "AQIDBA==",
"expiresAtMs": 1781441990538
}
}
Возможные state
createdapprovedrejectedcanceledexpired
5.8. CancelTrustedDeviceLogin
Операция для нового устройства, которое уже создало pairing-заявку и хочет принудительно снять ожидание до истечения TTL.
Запрос
{
"op": "CancelTrustedDeviceLogin",
"requestId": "esp-cancel-001",
"payload": {
"pairingId": "base64url",
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY"
}
}
Успешный ответ
{
"op": "CancelTrustedDeviceLogin",
"requestId": "esp-cancel-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "canceled"
}
}
Ошибки
400 / EMPTY_PAIRING_ID400 / EMPTY_REQUESTER_SESSION_KEY400 / BAD_REQUESTER_SESSION_KEY404 / PAIRING_NOT_FOUND422 / PAIRING_OF_ANOTHER_REQUESTER422 / PAIRING_NOT_PENDING