docs: reorganize system knowledge by domain

This commit is contained in:
2026-09-12 14:45:42 +03:00
parent 3a99af1a6e
commit f20c3f47bc
83 changed files with 2229 additions and 7335 deletions
+122
View File
@@ -0,0 +1,122 @@
# Аутентификация и сессии
Аутентификация связывает локального [пользователя](../../glossary.md#user)
с его сессией WealthLedger. Она открывает доступ к приложению; доступ
к брокерским данным определяется отдельным
[подключением T-Bank](../../integrations/tbank.md). Backend проверяет
учётные данные и управляет сессиями, frontend восстанавливает состояние
входа и выбирает доступные маршруты.
## Регистрация и вход
Пользователь регистрируется или входит по email и паролю. Регистрация сразу
создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся
к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее
72 UTF-8 байтов.
**Известное расхождение:** сохранённое требование регистрации и проверка
в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах.
Не-ASCII пароль может пройти форму и получить отказ API. Это не принятое
изменение password policy; требуется отдельное согласованное решение
о проверке пароля и подтверждение полного HTTP-потока.
Access token действует 15 минут и хранится только в памяти браузера.
Refresh token имеет серверный срок 7 дней, заменяется при восстановлении
сессии и передаётся в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`.
Сроки определены в [спецификации сессий](../../../specs/openspec/specs/user-authentication/spec.md#requirement-access-и-refresh-сессии)
и [ADR 006](../../adr/006-auth-session-policy.md).
После перезагрузки SPA получает новый access token через refresh-cookie.
Пока результат неизвестен, пользователь видит нейтральный skeleton каркаса
без преждевременного показа public или protected содержимого. После
завершения восстановления приложение выбирает доступный маршрут.
Все маршруты, кроме `/login` и `/register`, требуют сессию. После входа
пользователь возвращается на исходный внутренний маршрут, если он был.
Logout доступен в настройках: frontend очищает локальную сессию независимо
от состояния сети, а backend отзывает серверную сессию при предъявлении
её действительного refresh-cookie.
## Восстановление сессии
Динамическая схема показывает восстановление после reload между SPA, auth API
и локальным хранилищем сессий. SQLite здесь — файл базы backend.
```mermaid
sequenceDiagram
participant F as React SPA
participant B as Backend API
participant D as Локальная SQLite
Note over F: Reload: access token в памяти отсутствует
F->>B: Refresh с httpOnly cookie
B->>D: Проверить Session и текущий hash token
alt Действительная сессия, ротация выиграна
B->>D: Атомарно заменить hash и срок
B-->>F: Access token, профиль, новая refresh-cookie
F->>B: Protected request с Bearer
else Cookie нет или refresh отклонён
B-->>F: 401 и очистка cookie
Note over F: Готовое неавторизованное состояние
end
```
В одном экземпляре frontend одновременные refresh используют один
незавершённый запрос. Между вкладками используется Web Locks, если браузер
его поддерживает. Backend всё равно защищает ротацию атомарно: координация
браузеров не заменяет серверную проверку.
Обычный защищённый запрос с `401 UNAUTHORIZED` проходит refresh и один повтор
через общий транспорт; auth endpoints из этого цикла исключены. Само
истечение access token не запускает фоновый таймер refresh.
## HTTP-граница
Endpoints, статусы и формы публичных ответов определяет backend OpenAPI;
согласование производного клиента описано в
[contract workflow](../../development/runtime-openapi-verification.md).
Refresh-cookie ограничена auth API. Признак Secure зависит от режима
окружения: локальный HTTP предполагает development mode, см.
[локальное окружение](../../development/local-workspace.md).
HTTP-ошибка и транспортная ошибка refresh одинаково завершают начальное
восстановление готовым неавторизованным состоянием. При refresh после
защищённого `401` frontend очищает сессию в памяти и query cache, сохраняя
исходный `401` для перехода ко входу. Общая форма ошибок и известное
расхождение деталей валидации находятся в
[HTTP API](../../architecture/http-api.md#error-contract).
## Ограничения
Описанный auth flow ограничен регистрацией, входом, восстановлением сессии
и logout. Подтверждение email, восстановление или смена пароля, смена email,
удаление аккаунта, список устройств, logout всех устройств и rate limit
попыток входа не входят в эту реализованную возможность. Access token
может оставаться действительным до 15 минут после logout.
Если несколько клиентов одновременно предъявляют один действительный
refresh token, сервер завершает одну ротацию успешно, остальные запросы
получают `401`. Сессия победившего запроса остаётся действительной. Уже
заменённый refresh token также даёт `401`, но сам по себе не отзывает сессию:
механизм не хранит историю token или grace-window, чтобы отличить
конкурентный запрос от replay. Это ограничение и его причины сохранены
в [ADR 009](../../adr/009-concurrent-refresh-rotation-policy.md).
Refresh-cookie не задаёт собственные `maxAge` или `expires`.
Семидневный серверный срок refresh token не является обещанием сохранения
входа после закрытия браузера.
## Ответственность и проверка
Backend проверяет учётные данные и tokens, хранит состояние Session и
атомарно выполняет ротацию. Frontend управляет состоянием входа,
восстановлением после reload и доступностью экранов; общий HTTP-транспорт
передаёт Bearer token и выполняет описанный refresh/retry.
Проверки конкурентной ротации, браузерного состояния и транспорта охватывают
разные границы. Их изолированная проверка не заменяет полный HTTP-поток
и проверку атомарности с реальной изолированной базой.
Согласованное поведение находится в workspace-спецификации
[User authentication](../../../specs/openspec/specs/user-authentication/spec.md).
Правила начального ожидания — в
[состояниях интерфейса](../../architecture/cross-cutting/ui-resilience.md).