Добавить ESP pairing через доверенные сессии

This commit is contained in:
AidarKC
2026-06-14 18:21:23 +04:00
parent 5d13112b00
commit 3e04727022
36 changed files with 2500 additions and 6 deletions
+235
View File
@@ -7,6 +7,18 @@
- `ListSessions` — получить список активных сессий пользователя;
- `CloseActiveSession` — закрыть одну из активных сессий.
Дополнительно в этом же слое управления сессиями появился сценарий pairing через доверенную уже авторизованную сессию пользователя:
- `UpsertEspPairingSettings`
- `ListEspPairingRequests`
- `ApproveEspPairing`
- `RejectEspPairing`
Анонимное новое устройство работает с двумя связанными операциями:
- `StartEspPairing`
- `GetEspPairingStatus`
Логика раздела такая:
- сначала пользователь проходит `SessionLogin`;
@@ -151,3 +163,226 @@ K9v3nQ4u8jYk0a2p7cD4mLx1zR0sT5wV6bN8eH3fQ1M
Важно: это **не человеко-читаемое имя**, а непрозрачный идентификатор.
Нужно передавать его как есть, без нормализации регистра и без URL-экранирования внутри JSON.
---
## 5. ESP pairing через доверенную сессию
Этот блок относится к сценарию добавления новой сессии через доверенное устройство пользователя.
### 5.1. `UpsertEspPairingSettings`
Доступно для любой уже авторизованной доверенной сессии пользователя.
### Запрос
```json
{
"op": "UpsertEspPairingSettings",
"requestId": "esp-set-001",
"payload": {
"enabled": true,
"passwordHash": "argon2id$...",
"ttlSeconds": 180
}
}
```
### Успешный ответ
```json
{
"op": "UpsertEspPairingSettings",
"requestId": "esp-set-001",
"status": 200,
"ok": true,
"payload": {
"enabled": true,
"ttlSeconds": 180
}
}
```
### Ошибки
- `400 / EMPTY_PASSWORD_HASH` — попытка включить pairing без `passwordHash`.
- `463 / PAIRING_REQUIRES_AUTH_SESSION` — операция вызвана без уже авторизованной доверенной сессии пользователя.
### 5.2. `StartEspPairing`
Эта операция доступна без уже существующей пользовательской сессии.
### Запрос
```json
{
"op": "StartEspPairing",
"requestId": "esp-start-001",
"payload": {
"login": "alice",
"passwordHash": "argon2id$...",
"requesterSessionKey": "ed25519/BASE64_PUBLIC_KEY",
"requesterSessionType": 1,
"requesterClientPlatform": "Android",
"payloadType": 1
}
}
```
Поле `trustedSessionOnline` показывает, что у пользователя сейчас есть хотя бы одна онлайн доверенная сессия, способная принять pairing-заявку.
### Успешный ответ
```json
{
"op": "StartEspPairing",
"requestId": "esp-start-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "created",
"shortCode": "4920709",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"expiresAtMs": 1781441990538,
"trustedSessionOnline": true
}
}
```
### Ошибки
- `400 / EMPTY_LOGIN`
- `400 / EMPTY_PASSWORD_HASH`
- `400 / EMPTY_REQUESTER_SESSION_KEY`
- `400 / BAD_REQUESTER_SESSION_KEY`
- `400 / BAD_SESSION_TYPE`
- `400 / BAD_PAYLOAD_TYPE`
- `422 / PAIRING_NOT_AVAILABLE`
- `422 / PAIRING_PASSWORD_INVALID`
- `429 / PAIRING_RATE_LIMITED`
### 5.3. `ListEspPairingRequests`
Доступно для любой уже авторизованной доверенной сессии пользователя.
### Успешный ответ
```json
{
"op": "ListEspPairingRequests",
"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": "4920709",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"createdAtMs": 1781441810538,
"expiresAtMs": 1781441990538,
"deliveredToHomeserver": true
}
]
}
}
```
### Ошибки
- `463 / PAIRING_REQUIRES_AUTH_SESSION`
### 5.4. `ApproveEspPairing`
Доступно для любой уже авторизованной доверенной сессии пользователя.
### Запрос
```json
{
"op": "ApproveEspPairing",
"requestId": "esp-approve-001",
"payload": {
"pairingId": "base64url",
"encryptedPayload": "BASE64_OR_OTHER_OPAQUE_PAYLOAD"
}
}
```
### Успешный ответ
```json
{
"op": "ApproveEspPairing",
"requestId": "esp-approve-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "approved"
}
}
```
### Ошибки
- `400 / EMPTY_PAIRING_ID`
- `400 / EMPTY_ENCRYPTED_PAYLOAD`
- `404 / PAIRING_NOT_FOUND`
- `422 / PAIRING_OF_ANOTHER_USER`
- `422 / PAIRING_NOT_PENDING`
- `422 / PAIRING_EXPIRED`
- `463 / PAIRING_REQUIRES_AUTH_SESSION`
### 5.5. `RejectEspPairing`
Доступно для любой уже авторизованной доверенной сессии пользователя. Похоже на approve, но переводит заявку в `state=rejected`.
### 5.6. `GetEspPairingStatus`
Операция для нового устройства.
### Запрос
```json
{
"op": "GetEspPairingStatus",
"requestId": "esp-status-001",
"payload": {
"pairingId": "base64url"
}
}
```
### Успешный ответ после approve
```json
{
"op": "GetEspPairingStatus",
"requestId": "esp-status-001",
"status": 200,
"ok": true,
"payload": {
"pairingId": "base64url",
"state": "approved",
"shortCode": "4920709",
"fingerprintB58": "ASvYDPQidnAroKzQjtCjTuEQE8ckktV5nmmhYRhDzGaA",
"payloadType": 1,
"encryptedPayload": "AQIDBA==",
"expiresAtMs": 1781441990538
}
}
```
### Возможные `state`
- `created`
- `approved`
- `rejected`
- `expired`