Метод POST /api/v1/auth/logout

Документация API: Деактивация сессии пользователя (Logout) с каскадной инвалидацией WebSocket-дескрипторов

Published

July 2, 2026

1 Функциональное назначение

Метод предназначен для корректного и безопасного завершения текущей пользовательской сессии в экосистеме BUPAR.

В рамках новой распределенной архитектуры метод POST /logout решает три ключевые задачи: 1. Аннулирование долгоживущей сессии (Revocation): Переводит флаг is_revoked целевого refresh_token в состояние true в таблице user_sessions, полностью блокируя возможность фонового продления сессии через Dio Interceptor. 2. Очистка ресурсов WebSocket-менеджера: Отправляет внутренний асинхронный сигнал в оперативную память шлюза FastAPI, чтобы принудительно закрыть текущий активный WSS-стрим (/ws/push-stream/{id}) именно для этого девайса, предотвращая утечки памяти и отправку системных пушей на отключенное устройство. 3. Безопасный сброс стейта UI: Служит триггером для мобильного приложения Flutter для полной очистки локальных хранилищ Flutter Secure Storage и переключения интерфейса на стартовый экран авторизации.

2 Протокол взаимодействия (HTTP Контракт)

  • Метод: POST
  • Маршрут: /api/v1/auth/logout
  • Формат данных: application/json

2.1 Спецификация заголовков (HTTP Headers)

Заголовок Обязательный Описание Пример значения
Content-Type Да Указывает на передачу JSON-пакета application/json
Authorization Да Токен доступа (Access Token). Шлюз вычитывает user_id и home_group_id. Bearer eyJhbGciOiJIUzI1Ni...
X-Request-ID Да Сквозной ID запроса для трассировки сессии выхода из системы req-auth-out-77aa

2.2 Спецификация тела запроса (Request Body)

В теле запроса клиент явно передает текущий валидный refresh_token, который подлежит уничтожению в базе данных.

Поле Тип Обязательный Описание Пример значения
refresh_token String Да Токен обновления, который необходимо аннулировать в СУБД ref-token-xyz789...

2.2.1 Пример сырого JSON-запроса (Payload):

{
  "refresh_token": "ref-token-xyz789_v1_signature..."
}

3 Схема обработки запроса пользователя (Mermaid)

На диаграмме представлена логика безопасного выхода из системы (POST /logout). Сервис Auth блокирует refresh_token в PostgreSQL, после чего шлюз FastAPI обращается к своему пулу активных соединений и принудительно гасит WebSocket-стрим этого смартфона, освобождая оперативную память сервера.

sequenceDiagram
    autonumber
    actor User as Пользователь (Настройки -> Выход)
    participant APP as Мобильное приложение (Flutter)
    participant AS as Сервис Авторизации (FastAPI)
    participant DB as Реляционная СУБД (PostgreSQL)

    User->>APP: Нажимает кнопку "Выйти из аккаунта"
    APP->>AS: HTTP POST /api/v1/auth/logout (JSON с refresh_token и JWT в Header)
    activate AS
    
    note over AS: Шаг 3: Декодирование JWT (Выделение user_id и home_group_id)
    
    Note over AS, DB: Старт атомарной SQL-транзакции
    AS->>DB: UPDATE user_sessions SET is_revoked = true WHERE token = refresh_token
    activate DB
    DB-->>AS: Успешное обновление (Сессия заблокирована)
    deactivate DB
    Note over AS, DB: Коммит атомарной транзакции (COMMIT)
    
    note over AS: Шаг 6: Принудительное закрытие WSS дескриптора этого устройства
    AS->>APP: Системный фрейм WebSocket.close(1000, "LOGOUT_CONFIRMED")
    
    AS-->>APP: HTTP 200 OK (status: "success", data: "SESSION_TERMINATED")
    deactivate AS
    note over APP: Flutter стирает Secure Storage и переключается на экран ввода логина

4 Расшифровка шагов

  • Шаг 1 (User -> APP): Пользователь переходит в настройки профиля в мобильном приложении Flutter, нажимает кнопку «Выйти из аккаунта» и подтверждает действие.
  • Шаг 2 (APP -> AS): Сетевой слой приложения отправляет запрос HTTP POST /api/v1/auth/logout. В теле JSON передается уничтожаемый refresh_token, а в заголовке Authorization прокидывается живой Access Token сессии для прохождения контекстных фильтров шлюза.
  • Шаг 3 (AS -> AS): Микросервис Авторизации (FastAPI) принимает пакет, декодирует Access Token и извлекает из него user_id и home_group_id для проверки матрицы прав.
  • Шаг 4 (AS -> DB): Сервис открывает атомарную SQL-транзакцию в PostgreSQL и выполняет команду принудительной блокировки токена: UPDATE user_sessions SET is_revoked = true WHERE token = $1 AND user_id = $2. Параметр user_id берется строго из JWT, что исключает коллизию, когда один пользователь пытается аннулировать сессию другого.
  • Шаг 5 (DB -> AS): База данных фиксирует изменения и возвращает шлюзу подтверждение успешного апдейта строки. Транзакция закрывается командой COMMIT. С этого момента фоновое продление сессии через Dio Interceptor (Метод 3) для данного токена полностью заблокировано.
  • Шаг 6 (AS -> AS): Каскадное тушение сокетов: Шлюз FastAPI обращается к своему резидентному пулу соединений active_connections[home_group_id] и находит дескриптор WebSocket-канала, открытый с текущего смартфона пользователя.
  • Шаг 7 (AS -> APP): Сервер отправляет в сокет системный фрейм закрытия с кодом 1000 (Normal Closure) и текстовой директивой "LOGOUT_CONFIRMED", после чего корректно разрывает TCP-соединение, полностью вычищая дескриптор из оперативной памяти шлюза. Это освобождает системные ресурсы сервера от «мертвых» удерживаемых линков.
  • Шаг 8 (AS -> APP): HTTP-поток шлюза возвращает мобильному приложению успешный статус HTTP 200 OK. Приемник Flutter ловит ответ, полностью очищает локальный кэш и хранилища Flutter Secure Storage, сбрасывает стейты Bloc-менеджеров и переключает интерфейс смартфона на стартовое окно авторизации.

5 Спецификация ответов сервера (Response Body) и ошибок

5.1 1. Успешный ответ (Success Response)

5.1.1 HTTP 200 OK (Ответ на Шаге 8)

Возвращается мобильному приложению после успешного коммита транзакции деактивации. В теле ответа фиксируется успешное уничтожение сессии.

  • Заголовки ответа (Response Headers):
    • Content-Type: application/json
  • Тело ответа (Response Body):
{
  "status": "success",
  "data": {
    "message": "SESSION_TERMINATED",
    "details": "Refresh token successfully revoked. Active WebSocket connection terminated clean.",
    "timestamp": "2026-07-02T02:20:00Z"
  }
}

5.2 2. Вилки исключений и обработка ошибок

5.2.1 Ошибка: Токен не найден или уже аннулирован (HTTP 404 Not Found)

Выбрасывается, если переданный в JSON-теле refresh_token отсутствует в таблице user_sessions, либо если его флаг is_revoked уже равен true (например, метод logout был вызван повторно).

  • Заголовки ответа (Response Headers):
    • Content-Type: application/json
  • Тело ответа (Response Body):
{
  "error_code": "ERR-LOGOUT-TARGET-NOT-FOUND",
  "message": "Указанная сессия обновления не найдена или была аннулирована ранее.",
  "details": {
    "action": "Proceed with local state clearance in Flutter securely as session is already dead on server."
  }
}

5.2.2 Ошибка валидации структуры (HTTP 422 Unprocessable Entity)

Выбрасывается бэкенд-шлюзом FastAPI при передаче пустого поля токена.

{
  "error_code": "ERR-VALIDATION-FAILED",
  "message": "Передан некорректный или пустой токен для аннулирования сессии.",
  "details": [
    {
      "loc": ["body", "refresh_token"],
      "msg": "Field constraints violation: string cannot be empty",
      "type": "value_error.str.min_length"
    }
  ]
}