---
title: Логин по email и выдача JWT (Backend)
---

# Сценарий 1.1: Логин и JWT

## Бизнес-цель

Пользователь сайта или приложения входит по **email и паролю**. Система проверяет учётные данные, обновляет отметку последнего входа и выдаёт **JWT**, чтобы дальнейшие запросы к API выполнялись от имени этого пользователя без хранения сессии на сервере (stateless API).

## Точки входа

| Тип | Метод + URL | Класс |
| --- | --- | --- |
| HTTP | `POST /user/login` | `App\Controller\UserController::login` |
| HTTP | `GET /user/logout` | `App\Controller\UserController::logout` (ответ-заглушка; инвалидация токена на сервере не показана в коде) |
| HTTP | `GET /user/` | `App\Controller\UserController::index` — текущий пользователь по JWT (`#[IsGranted('ROLE_USER')]`) |

Асинхронные сообщения и CLI для этого сценария **не используются**.

## Пошаговый алгоритм (flow)

1. Клиент отправляет JSON с полями `username` (фактически email) и `password` (см. OpenAPI в контроллере).
2. `UserController::login` вручную парсит тело; при отсутствии полей отвечает `400` с текстом `Missing credentials`.
3. Значения кладутся в `App\Dto\UserLoginDto`; срабатывает Symfony Validator по атрибутам DTO.
4. Вызывается `App\Service\User\AuthenticationService::jsonAuth($dto)`:
   - в `App\Repository\UserRepository` выполняется поиск `User` по **`email = md5(введённый email)`** (в БД в колонке `users.email` хранится **хэш**, а не открытый email);
   - пароль проверяется через `UserPasswordHasherInterface::isPasswordValid`.
5. При неверной паре или отсутствии пользователя контроллер возвращает `400` с сообщением о неверных учётных данных.
6. При успехе вызывается `$user->updateLoggedIn()`, `EntityManager::flush()` — в БД обновляется поле `loggedIn`.
7. Lexik JWT: `JWTTokenManagerInterface::create($user)` формирует токен; в ответ уходит JSON: `successful`, `token`, `user` (массив из `User::toArray()` — `uid`, `bdate`, `roles`, `regionId`, `loggedIn`).

## Mermaid

```mermaid
sequenceDiagram
    participant C as Клиент
    participant UC as UserController
    participant V as Validator
    participant AS as AuthenticationService
    participant UR as UserRepository
    participant EM as EntityManager
    participant JWT as JWTTokenManagerInterface

    C->>UC: POST /user/login {username, password}
    UC->>V: validate(UserLoginDto)
    V-->>UC: ок / ошибки
    UC->>AS: jsonAuth(dto)
    AS->>UR: findOneBy email = md5(dto.email)
    UR-->>AS: User | null
    AS-->>UC: {user, isPasswordValid}
    alt неверный пароль или нет пользователя
        UC-->>C: 400
    else успех
        UC->>EM: flush (updateLoggedIn)
        UC->>JWT: create(User)
        UC-->>C: 200 {token, user}
    end
```

## Внешние зависимости

| Система | Участие |
| --- | --- |
| PostgreSQL | таблица `users` (`App\Entity\User`) |
| Lexik JWT | генерация и последующая валидация токена |
| Bitrix / Инфоклиника / Redis / SMS | **не задействованы** в этом сценарии |

## Обработка ошибок и edge cases

- **Нет полей credentials** — `400`, `Missing credentials`.
- **Ошибки валидации DTO** — `400`, `successful: false`, `errors` (строка нарушений).
- **Неверный email/пароль** — единый ответ `400` с текстом «Не правильное имя пользователя или пароль».
- **Согласованность с JWT**: `App\Service\DecoderJWT\JWTDecoderService::getUser()` восстанавливает пользователя по `username` из payload и полю `email` в БД — это тот же «логин», что хранится в `User` после регистрационных сценариев (часто `md5(...)`).

## Ссылки на классы

- `apps/backend/src/Controller/UserController.php`
- `apps/backend/src/Service/User/AuthenticationService.php`
- `apps/backend/src/Dto/UserLoginDto.php`
- `apps/backend/src/Entity/User.php`
- `apps/backend/src/Repository/UserRepository.php`
- Конфигурация security/JWT: `apps/backend/config/packages/security.yaml`, `lexik_jwt_authentication.yaml`

См. также [backend-ddd.md](../backend-ddd.md).
