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

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

Published

July 2, 2026

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

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

В рамках новой распределенной архитектуры метод 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 App as APP (Мобильное приложение)
    participant Nginx as Nginx Proxy
    participant GW as GATEWAY (backend-api)
    participant Auth as AUTH (auth-service)
    participant Redis as REDIS (Token Blacklist)
    participant DB as DB_AUTH (PostgreSQL)

    App->>Nginx: Шаг 1: POST /api/v1/auth/logout (Bearer Access / Body Refresh)
    activate Nginx
    Nginx->>GW: Шаг 2: POST /backend-api/v1/auth/logout
    activate GW
    GW->>Auth: Шаг 3: gRPC: LogoutUser(LogoutRequest)
    activate Auth
    
    Auth->>Redis: Шаг 4: Redis.SET(blacklist:access, ex=TTL)
    activate Redis
    note over Auth, Redis: При падении Redis:<br/>Fail-Close -> Аварийный выход HTTP 503
    Redis-->>Auth: Токен успешно заблокирован в кэше
    deactivate Redis
    
    Auth->>DB: Шаг 5: SQL UPDATE user_sessions SET is_revoked = true...
    activate DB
    DB-->>Auth: Статус сессии обновлен в БД
    deactivate DB
    
    Auth-->>GW: Шаг 6: gRPC: LogoutResponse
    deactivate Auth
    GW-->>App: Шаг 7: HTTP 200 OK (status: success)
    deactivate GW
    deactivate Nginx
    
    note over App: Шаг 8: Внутренний метод<br/>SecureStorage.deleteAll() и редирект

Процесс безопасного выхода (Logout)

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

Шаг Действие Параметры Ошибки (Исключения / Статусы)
Шаг 1 (APP -> NGINX) Пользователь нажимает «Выход». Приложение отправляет запрос, прикрепляя текущий Access Token в заголовок, а Refresh Token в тело. HTTP POST /api/v1/auth/logout
Headers: Bearer (Access)
Body: {"refresh_token": "..."}
DioException: no internet connection
Шаг 2 (NGINX -> GATEWAY) Nginx пересылает запрос деактивации на API Gateway. HTTP POST /backend-api/v1/auth/logout HTTP 502 Bad Gateway
Шаг 3 (GATEWAY -> AUTH) API Gateway проверяет метаданные и отправляет gRPC-команду на полное аннулирование сессии. gRPC: LogoutUser(LogoutRequest) HTTP 422 Unprocessable Entity
Шаг 4 (AUTH -> REDIS) Сервис мгновенно заносит Access Token в Redis Blacklist с временем жизни (TTL), равным остатку его действия. Вызов: Redis.SET(f"blacklist:{access_jti}", ..., ex=TTL) Redis: ConnectionRefusedError
gRPC: UNAVAILABLE \(\rightarrow\) Fail-Close! HTTP 503
Шаг 5 (AUTH -> DB_AUTH) Сервис обновляет статус долгоживущей сессии в PostgreSQL, переводя признак is_revoked в true. SQL UPDATE user_sessions SET is_revoked = true WHERE refresh_token_jti = $1 PostgreSQL: connection pool timeout
Шаг 6 (AUTH -> GATEWAY) Контур авторизации подтверждает успешный отзыв всех ключей доступа и сессий. gRPC: LogoutResponse gRPC: INTERNAL
Шаг 7 (GATEWAY -> APP) API Gateway возвращает статус успешного выполнения операции на мобильный клиент. HTTP 200 OK
Body: {"status": "success"}
HTTP 500 Internal Server Error
Шаг 8 (APP -> APP) Мобильное приложение полностью очищает Secure Storage от токенов и принудительно перенаправляет пользователя на экран авторизации. Вызов: SecureStorage.deleteAll() SecureStorageException

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"
    }
  ]
}