sequenceDiagram
autonumber
actor User as Пользователь (Экран Авторизации)
participant APP as Мобильное приложение (Flutter)
participant AS as Сервис Авторизации (FastAPI)
participant DB as Реляционная СУБД (PostgreSQL)
User->>APP: Вводит email/пароль, нажимает "Войти"
APP->>AS: HTTP POST /api/v1/auth/login (JSON с username и password)
activate AS
AS->>DB: SELECT id, password_hash, home_group_id, account_type FROM users WHERE email = username
activate DB
DB-->>AS: Возвращает метаданные и хэш пароля пользователя
deactivate DB
note over AS: Шаг 5: Сверка хэша пароля (Bcrypt/Argon2)
alt Хэши совпали (Успешный вход)
note over AS: Шаг 6: Генерация JWT и упаковка Claims (home_group_id, account_type)
AS-->>APP: HTTP 200 OK (status: "success", token: "eyJ...", expires_in: 3600)
else Пароль неверный ИЛИ пользователь не найден
AS-->>APP: HTTP 401 Unauthorized (ERR-INVALID-CREDENTIALS)
end
deactivate AS
note over APP: Flutter сохраняет токен в Secure Storage и открывает экран Холодильника
Метод POST /api/v1/auth/login
Документация API: Аутентификация пользователя (Login) с генерацией JWT, содержащего Multi-Tenancy клеймы
- INVENTORY-2XX Backlog — Краткое Описание задачи 1.
- INVENTORY-1XX Refinement (Уточнение) — Краткое Описание задачи 2.
1 Функциональное назначение
Метод предназначен для аутентификации пользователя в системе по паре логин/пароль.
В рамках новой архитектуры этот метод выполняет роль транслятора контекста безопасности (Security Context Provider):
- Генерация токена сессии: Проверяет хэш пароля в СУБД и генерирует криптографически подписанный
Access Token(JWT) [2026-07-02]. - Внедрение клеймов изоляции (JWT Claims): Намертво зашивает внутрь Payload токена параметры
home_group_id,account_typeиuser_id. Это избавляет основные транзакционные микросервисы (Cook,Consume,Waste) от необходимости делать лишние JOIN-запросы в базу данных пользователей при каждом списании продуктов, так как шлюз FastAPI вычитывает контекст группы прямо из расшифрованного токена.
2 Протокол взаимодействия (HTTP Контракт)
- Метод:
POST - Маршрут:
/api/v1/auth/login - Формат данных:
application/json
2.1 Спецификация заголовков (HTTP Headers)
| Заголовок | Обязательный | Описание | Пример значения |
|---|---|---|---|
Content-Type |
Да | Формат передаваемых данных | application/json |
X-Request-ID |
Да | Сквозной ID запроса для трассировки сессии авторизации | req-auth-log-44bb |
2.2 Спецификация тела запроса (Request Body)
| Параметр | Тип | Обязательный | Описание | Пример значения |
|---|---|---|---|---|
username |
String | Да | Уникальный логин пользователя (его email-адрес) | user@somedomain.kz |
password |
String | Да | Сырой текстовый пароль для сверки хэша | MySecretPassword123 |
2.2.1 Пример сырого JSON-запроса (Payload):
{
"username": "user@somedomain.kz",
"password": "MySecretPassword123"
}2.3 Спецификация успешного ответа (Response Body)
2.3.1 HTTP 200 OK
Возвращается при успешном совпадении учетных данных.
{
"status": "success",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3ItODgyMiIsImhvbWVfZ3JvdXBfaWQiOiJncm91cC03NzItYWxwaGEiLCJhY2NvdW50X3R5cCI6IkhPTUUiLCJleHAiOjE3ODMzMDk2MDB9.signature...",
"expires_in": 3600
}3 Схема обработки запроса пользователя (Mermaid)
На диаграмме представлена логика аутентификации. Сервис Authorization сверяет хэш пароля в PostgreSQL, извлекает параметры привязки к пространству и генерирует JWT-токен, содержащий зашитые JWT Claims.
4 Расшифровка шагов и Анатомия JWT Payload
- Шаг 1 (
User -> APP): Пользователь на форме входа вводит свой email (в полеusername) и пароль, после чего нажимает кнопку «Войти». - Шаг 2 (
APP -> AS): Мобильное приложение упаковывает учетные данные в JSON и отправляет запросHTTP POST /api/v1/auth/login. В заголовках передается сквознойX-Request-ID. - Шаг 3 (
AS -> DB): Микросервис Авторизации (FastAPI) принимает запрос и обращается к базе данных:SELECT id, password_hash, home_group_id, account_type FROM users WHERE email = $1. - Шаг 4 (
DB -> AS): СУБД возвращает строку пользователя, включая хэшированный пароль и параметры Multi-Tenancy изоляции (к какому холодильнику привязан юзер и по каким правилам работает этот контур). - Шаг 5 (
AS -> AS): Метод проверяет соответствие сырого входящего пароля и хэша из базы данных с помощью криптографической библиотеки (Bcrypt/Argon2). Если пароли не совпадают, выполнение прерывается с возвратом ошибкиHTTP 401. - Шаг 6 (
AS -> AS): В случае успеха сервис генерирует строкуAccess Tokenпо стандарту JWT (симметричное шифрование HS256 с использованием системного приватного ключа). Внутрь полезной нагрузки (Payload) токена жестко запекаются три наших архитектурных кита.
4.1 Анатомическая структура декодированного JWT Payload (Claims):
Когда транзакционный бэкенд-шлюз или сторонний ИИ-микросервис расшифровывают токен, они без обращения к БД видят следующий стандартизированный JSON-датасет:
{
"sub": "usr-8822-fa41",
"home_group_id": "group-772-alpha",
"account_type": "HOME",
"iss": "auth-service",
"iat": 1783306000,
"exp": 1783309600
}sub(Subject) — уникальныйuser_idавтора операции для логирования действий.home_group_id— UUID/строковый идентификатор целевого холодильника семьи или офиса. По нему фильтруются все SQL-запросыWHERE home_group_id = $1.account_type— флаг контура (HOMEилиOFFICE), управляющий вилками ухода в виртуальный минус или жесткой блокировки дефицита.exp(Expiration Time) — время жизни токена (3600 секунд / 1 час), по истечении которого Flutter-клиент обязан запустить фоновое обновление сессии.
5 Спецификация вилок исключений и обработки ошибок
При аутентификации система обрабатывает ошибки безопасности на Шаге 5, не раскрывая наружу, какая именно часть учетных данных была неверной. Это защищает контур от перебора логинов злоумышленниками.
5.2 2. Ошибка валидации формата email (HTTP 422 Unprocessable Entity)
Возвращается на Шаге 4 бэкенд-шлюзом FastAPI, если переданная строка в поле username не соответствует валидному синтаксису адреса электронной почты.
{
"error_code": "ERR-VALIDATION-FAILED",
"message": "Передан некорректный формат логина (требуется валидный email).",
"details": [
{
"loc": ["body", "username"],
"msg": "value is not a valid email address",
"type": "value_error.email"
}
]
}