# Аутентификация и сессии Аутентификация связывает локального [пользователя](../../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`. Сроки определены в [спецификации сессий](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/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 ``` В одной вкладке одновременные refresh используют один незавершённый запрос; cookie-writing requests упорядочены локально. Межвкладочная синхронизация не поддерживается: после смены сессии в другой вкладке допустимы reload/re-login. Backend защищает ротацию атомарно. Неуспешный refresh не удаляет cookie победившего запроса; недействительный token по-прежнему не восстанавливает сессию. Обычный защищённый запрос с `401 UNAUTHORIZED` проходит refresh и один повтор через общий транспорт; auth endpoints из этого цикла исключены. Само истечение access token не запускает фоновый таймер refresh. ## HTTP-граница Endpoints, статусы и формы публичных ответов определяет backend OpenAPI; граница производного клиента описана в [HTTP API](../../architecture/http-api.md#источник-контракта). Refresh-cookie ограничена auth API. Признак Secure зависит от режима окружения: в development допускается локальный HTTP, в остальных режимах Secure-cookie требует защищённого соединения. 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](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/user-authentication/spec.md). Правила начального ожидания — в [состояниях интерфейса](../../architecture/cross-cutting/ui-resilience.md).