123 lines
9.0 KiB
Markdown
123 lines
9.0 KiB
Markdown
# Аутентификация и сессии
|
||
|
||
Аутентификация связывает локального [пользователя](../../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).
|