docs(spec): sessions API, device fields, profile sessions UI. Refs EventHub/EventHubBack#74
This commit is contained in:
+25
-11
@@ -260,7 +260,10 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
|
||||
- `calendar_share` (`read` | `write` | `admin`): соредактор personal / заместитель commercial;
|
||||
invite/accept/revoke + grants; personal **не** в search/discovery. Не путать с follow,
|
||||
specialist_invite и платной subscription.
|
||||
- `client_type=mobile` — хвост, когда появится нативный клиент.
|
||||
- Управление своими сессиями + `client_type`/`device_name` — **сделано** (Back#74):
|
||||
`GET/DELETE /v1/sessions`, `POST /v1/sessions/revoke-others`; login принимает
|
||||
`client_type` (`web`|`mobile`) и `device_name`, сохраняет `User-Agent`.
|
||||
UI списка — Front#72. Kick-on-login / нативный клиент — по-прежнему future.
|
||||
- **Не в фазе 2:** оплата услуги клиентом (B2C) — вне горизонта продукта.
|
||||
|
||||
### 2.1.3. Share / приглашения (соредактор и заместитель)
|
||||
@@ -574,11 +577,18 @@ src/
|
||||
`/invites?token=`, `/reset-password?token=` (бренд CalenTIQ).
|
||||
- `POST /v1/reset-password` — `{token, password}` (пароль ≥ 8 символов); успех → новый hash,
|
||||
токен удалён, refresh-сессии user отозваны; `404`/`410`/`400`/`403`.
|
||||
- `POST /v1/login` — вход.
|
||||
- `POST /v1/refresh` — обновление токена.
|
||||
- `POST /v1/login` — вход; опционально `client_type` (`web`|`mobile`, default `web`),
|
||||
`device_name` (max 120); `User-Agent` пишется в сессию. Ответ:
|
||||
`{ token, refresh_token, session_id, user }`. Неверный `client_type` → `400`.
|
||||
- `POST /v1/refresh` — обновление токена; ответ `{ token, refresh_token, session_id }`.
|
||||
- `POST /v1/logout` — `{ refresh_token }` → отзыв текущей `auth_session`; после этого
|
||||
refresh той же сессии → `401`. Access JWT до истечения TTL не отзывается.
|
||||
Невалидный refresh → `401`; отсутствие поля → `400`. Клиент чистит storage даже при ошибке сети.
|
||||
- `GET /v1/sessions` — список активных своих user-сессий (Bearer):
|
||||
`session_id`, `client_type`, `device_name`, `user_agent`, `created_at`, `updated_at`, `expires_at`.
|
||||
- `DELETE /v1/sessions/:id` — отзыв своей сессии; чужая/нет → `404`.
|
||||
- `POST /v1/sessions/revoke-others` — Bearer + `{ refresh_token }`: оставить сессию
|
||||
из refresh, отозвать остальные; чужой subject → `403`; невалидный refresh → `401`.
|
||||
- `GET /v1/user/me` — профиль пользователя.
|
||||
- `PATCH /v1/user/me` — частичное обновление своего профиля: `language` (`ru`|`en`),
|
||||
`nickname`, `timezone`, `phone`, `avatar_url`, `preferences`; смена пароля —
|
||||
@@ -721,43 +731,47 @@ src/
|
||||
| `current_jti` | Актуальный jti refresh JWT |
|
||||
| `expires_at` | Срок жизни сессии (30 дней) |
|
||||
| `revoked` | Флаг отзыва |
|
||||
| `device_name` | Человекочитаемая метка устройства (с login; может быть пустой) |
|
||||
| `user_agent` | `User-Agent` с login (может быть пустой) |
|
||||
| `created_at` / `updated_at` | Создание / последняя ротация или отзыв |
|
||||
|
||||
**Refresh JWT (admin)** — claims:
|
||||
- `typ=refresh`, `aud=admin`, `sub=<admin_id>`
|
||||
- `sid=<session_id>`, `fid=<family_id>`, `jti=<current_jti>`
|
||||
- `client=admin`, `exp`, `iat`
|
||||
|
||||
**Refresh JWT (user, фаза 2)** — claims:
|
||||
**Refresh JWT (user)** — claims:
|
||||
- `typ=refresh`, `aud=user`, `sub=<user_id>`
|
||||
- `sid`, `fid`, `jti` — как у admin
|
||||
- `client=web` (фаза 3: `mobile`), `exp`, `iat`
|
||||
- `client=web` \| `mobile` (из сессии; refresh не меняет device/UA)
|
||||
- Подпись: `JWT_SECRET` (user JWK), отдельно от admin
|
||||
|
||||
**Поток login admin** (`POST /v1/admin/login`):
|
||||
1. Проверка email/password.
|
||||
2. Создание записи `auth_session`.
|
||||
3. Ответ: `{ token, refresh_token, user }`.
|
||||
3. Ответ: `{ token, refresh_token, session_id, user }`.
|
||||
|
||||
**Поток refresh admin** (`POST /v1/admin/refresh`):
|
||||
1. Верификация подписи и срока refresh JWT.
|
||||
2. Сверка `jti` из JWT с `current_jti` в Mnesia.
|
||||
3. При совпадении — ротация: новый `jti`, новая пара токенов.
|
||||
3. При совпадении — ротация: новый `jti`, новая пара токенов + `session_id`.
|
||||
4. При несовпадении (reuse) — `revoke_family`, ответ 401.
|
||||
|
||||
**Поток login user** (`POST /v1/login`):
|
||||
1. Проверка email/password.
|
||||
2. Создание записи `auth_session` (`subject_type=user`, `client_type=web`).
|
||||
3. Ответ: `{ token, refresh_token, user }`.
|
||||
2. Создание `auth_session` (`subject_type=user`, `client_type`, `device_name`, `user_agent`).
|
||||
3. Ответ: `{ token, refresh_token, session_id, user }`.
|
||||
|
||||
**Поток refresh user** (`POST /v1/refresh`):
|
||||
1. Верификация refresh JWT (`aud=user`).
|
||||
2. Сверка `jti` с `current_jti` в Mnesia, ротация при успехе.
|
||||
2. Сверка `jti` с `current_jti` в Mnesia, ротация при успехе; `client` берётся из сессии.
|
||||
3. Reuse — `revoke_family`, 401.
|
||||
|
||||
**Фазы внедрения:**
|
||||
- Фаза 1 (#23): admin API — реализовано.
|
||||
- Фаза 2 (#26): user login + `/v1/refresh` — реализовано.
|
||||
- Фаза 3: client web + mobile (явный `client_type`, тот же контракт).
|
||||
- Фаза 3 (Back#74): явный `client_type` web|mobile, device meta, list/revoke sessions — реализовано.
|
||||
Kick-on-login / concurrent policy по `client_type` — не сделано.
|
||||
|
||||
### Legacy (не используется login/refresh user)
|
||||
- Таблица `session` (`ram_copies`) и opaque refresh в `core_session` — оставлены в кодовой базе, hot path user API переведён на `auth_session`.
|
||||
|
||||
Reference in New Issue
Block a user