sequenceDiagram
autonumber
participant APP as Flutter (Dio Interceptor)
participant AS as Сервис Авторизации (FastAPI)
participant DB as Реляционная СУБД (PostgreSQL)
Note over APP, AS: Ранее: любой транзакционный метод вернул HTTP 401
APP->>AS: HTTP POST /api/v1/auth/refresh (JSON с текущим refresh_token)
activate AS
AS->>DB: SELECT user_id, home_group_id, account_type, is_revoked FROM user_sessions WHERE token = refresh_token
activate DB
DB-->>AS: Возвращает метаданные сессии и статус токена
deactivate DB
note over AS: Шаг 4: Валидация срока жизни токена и флага is_revoked
alt Токен валиден (Успешная ротация)
AS->>DB: UPDATE user_sessions SET is_revoked = true WHERE token = old_token (Аннулирование)
AS->>DB: INSERT INTO user_sessions (user_id, token, expires_at) VALUES (Новая сессия)
note over AS: Шаг 7: Генерация нового Access Token (JWT) с перевыпуском Claims
AS-->>APP: HTTP 200 OK (status: "success", token: "eyJ...", expires_in: 3600)
else Токен просрочен ИЛИ уже был аннулирован (Reuse Detection)
note over AS: Шаг 9: Каскадный сброс всех сессий юзера при подозрении на взлом
AS->>DB: UPDATE user_sessions SET is_revoked = true WHERE user_id = target_user_id
AS-->>APP: HTTP 401 Unauthorized (ERR-REFRESH-TOKEN-EXPIRED)
end
deactivate AS
note over APP: Flutter обновляет токен в Secure Storage и бесшовно повторяет упавший запрос
Метод POST /api/v1/auth/refresh
Документация API: Фоновое обновление сессии (Refresh Token) с бесшовным перевыпуском Multi-Tenancy JWT Claims
- INVENTORY-2XX Backlog — Краткое Описание задачи 1.
- INVENTORY-1XX Refinement (Уточнение) — Краткое Описание задачи 2.
1 Функциональное назначение
Метод предназначен для безопасного продолжения активной пользовательской сессии без повторного принудительного ввода логина и пароля на смартфоне.
В рамках микросервисной архитектуры этот эндпоинт решает критические инфраструктурные задачи:
- Обслуживание Dio Interceptor: Метод вызывается сетевым слоем Flutter-приложения строго в фоновом режиме, когда шлюз возвращает ошибку
HTTP 401 Unauthorizedна любой транзакционный запрос. - Бесшовный перевыпуск контекста: Проверяет долгоживущий
Refresh Tokenпо базе сессий и генерирует новый короткоживущийAccess Token(JWT), сохраняя актуальные JWT Private Claimshome_group_id,account_typeиuser_id. Это гарантирует, что фоновые процессы сокетов и транзакции не прервутся для пользователя. - Механизм обновления токенов (Refresh Token Rotation): Для защиты от компрометации, при каждом вызове старый Refresh-токен аннулируется, а клиенту выдается новая пара токенов, что предотвращает атаки типа Replay Attack.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
POST - Маршрут:
/api/v1/auth/refresh - Формат данных:
application/json
2.1 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Content-Type |
Да | Указывает на передачу строго типизированного JSON-пакета | application/json |
X-Request-ID |
Да | Сквозной ID запроса для трассировки фоновой ротации сессии | req-auth-ref-22aa |
2.2 Спецификация тела запроса (Request Body)
В соответствии с требованиями безопасности, Refresh-токен передается в теле JSON-запроса (или извлекается из защищенных HttpOnly Cookies в зависимости от конфигурации окружения).
| Поле | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
refresh_token |
String | Да | Долгоживущий токен обновления, выданный при входе в систему | ref-token-xyz789... |
2.2.1 Пример сырого JSON-запроса (Payload):
{
"refresh_token": "ref-token-xyz789_v1_signature..."
}2.3 Спецификация успешного ответа (Response Body)
2.3.1 HTTP 200 OK
Возвращается при успешной валидации сессии. Выдается обновленный JWT-токен.
{
"status": "success",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3UrODgyMiIsImhvbWVfZ3JvdXBfaWQiOiJncm91cC03NzItYWxwaGEiLCJhY2NvdW50X3R5cCI6IkhPTUUiLCJleHAiOjE3ODMzMTMyMDB9.new_signature...",
"expires_in": 3600
}3 Схема обработки запроса пользователя (Mermaid)
На диаграмме представлена логика работы Dio Interceptor при фоновом обновлении токенов. Сервис Auth проверяет refresh_token по базе активных сессий, выполняет обновление, аннулирует старый ключ и выпускает новый JWT с сохранением JWT Private Claims.
4 Расшифровка шагов и логика ротации токенов
- Шаг 1 (
APP -> AS): Сетевой слой мобильного приложения (Dio Interceptor), перехватив ошибкуHTTP 401от транзакционного шлюза склада, замораживает очередь пользовательских запросов интерфейса. ИзFlutter Secure Storageизвлекается текущийrefresh_token, и отправляется запросHTTP POST /api/v1/auth/refresh. - Шаг 2 (
AS -> DB): Микросервис Авторизации (FastAPI) принимает пакет и выполняет прямой запрос к таблице сессийPostgreSQLдля проверки легитимности токена:SELECT user_id, home_group_id, account_type, is_revoked, expires_at FROM user_sessions WHERE token = $1. - Шаг 3 (
DB -> AS): База данных возвращает метаданные сессии, включая признаки компрометации (is_revoked) и скоуп Multi-Tenancy изоляции, к которому привязан данный токен. - Шаг 4 (
AS -> AS): Алгоритм бэкенда проводит комплексную валидацию: сверяет текущую дату сexpires_atи проверяет, не равен ли флагis_revokedзначениюtrue. Если проверки пройдены, запускается контур Token Rotation. - Шаг 5 (
AS -> DB): В целях безопасности старый использованныйrefresh_tokenнемедленно аннулируется в базе данных:UPDATE user_sessions SET is_revoked = true WHERE token = $1. Это защищает систему от повторного использования токена злоумышленником при перехвате трафика. - Шаг 6 (
AS -> DB): В рамках той же атомарной транзакции генерируется и записывается новая строка для следующего долгоживущего токена обновления, продлевая сессию устройства в СУБД. - Шаг 7 (
AS -> AS): Бэкенд заново собирает Payload для короткоживущегоAccess Token. Внутрь структуры JWT повторно упаковываются неизменяемые JWT Private Claims:home_group_id,account_typeиuser_id. Подпись токена обновляется криптографическим ключом. - Шаг 8 (
AS -> APP): Сервер возвращает статусHTTP 200 OKи передает новый JWT. Сетевой слой Flutter обновляет локальный кэш токенов, разблокирует очередь и бесшовно для пользователя повторяет тот транзакционный запрос (например, списание порции пирога), который изначально упал по ошибке 401. Пользователь не видит экранов авторизации и не замечает смены сессии. - Шаг 9 (
AS -> DB): Вилка обнаружения атаки (Reuse Detection): Если система видит, что клиент прислалrefresh_token, у которогоis_revoked == true, это сигнализирует о том, что токен уже был украден и использован ранее. Включается жесткая fail-safe блокировка: бэкенд аннулирует абсолютно все активные сессии данногоuser_idв базе данных, мгновенно выкидывая все смартфоны пользователя на экран логина, блокируя действия потенциального хакера.
5 Спецификация ответов сервера (Response Body) и ошибок
5.1 1. Спецификация структуры сессий PostgreSQL (Token Rotation Layer)
Для поддержки безопасного механизма обновления токенов и определения повторного использования угнанных ключей (Reuse Detection), база данных хранит метаданные сессий в изолированной таблице.
-- Таблица сессий и долгоживущих Refresh-токенов
CREATE TABLE user_sessions (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(50) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token VARCHAR(512) NOT NULL UNIQUE, -- Хэш или тело Refresh-токена
is_revoked BOOLEAN NOT NULL DEFAULT FALSE,-- Флаг аннулирования токена (использован/украден)
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Индекс для мгновенной верификации токена при фоновом обновлении Dio Interceptor
CREATE INDEX idx_user_sessions_lookup ON user_sessions(token) WHERE is_revoked = FALSE;5.2 2. Вилки исключений и обработка ошибок
5.2.2 Ошибка: Токен поврежден или имеет неверный формат (HTTP 422 Unprocessable Entity)
Выбрасывается бэкенд-шлюзом FastAPI, если переданная строка в поле refresh_token пустая или нарушает валидацию длины Pydantic-модели.
{
"error_code": "ERR-VALIDATION-FAILED",
"message": "Передан некорректный или пустой токен обновления refresh_token.",
"details": [
{
"loc": ["body", "refresh_token"],
"msg": "Field constraints violation: token string cannot be empty",
"type": "value_error.str.min_length"
}
]
}