Files
docs/apps/backend-scenarios/auth-uid-pcode.md
T

100 lines
6.2 KiB
Markdown

---
title: Авторизация по UID и pcode (Backend)
---
# Сценарий 1.2: Авторизация по **UID** и **pcode**
## Бизнес-цель
Часть пользователей приходит из медицинской системы (**Инфоклиника / MIS**): у пациента уже есть **числовой идентификатор** в сторонней системе. Сайт должен позволить:
- связать этот **UID** с учётной записью в API (`App\Entity\User::uid`);
- войти **по email+паролю**, если пользователь уже создан с тем же `uid`;
- войти **по pcode + дате рождения**, если личный кабинет строится на «медицинском» коде без ввода email на первом шаге.
## Что такое `uid` и `pcode` в коде
| Термин в API | Где в коде | Смысл |
| --- | --- | --- |
| `uid` | поле `User::$uid`, уникальное `int` | Идентификатор пациента/пользователя из внешней системы (в т.ч. MIS). |
| `pcode` в `POST /user/auth-by-pcode` | мапится в `UserUidAuthDto::$uid` | Тот же **числовой идентификатор**, что и `uid`; название «pcode» — контракт фронта/legacy. |
Откуда берутся значения **на практике**: из МИС/личного кабинета после идентификации пациента (конкретный HTTP-запрос к Инфоклинике **в этом приложении** для «получить pcode» в сценарии логина не вызывается — клиент передаёт уже известные `uid`/`pcode` и дату рождения).
## Точки входа
| Тип | Метод + URL | Класс |
| --- | --- | --- |
| HTTP | `POST /user/auth` | `UserController::auth` |
| HTTP | `POST /user/auth-by-pcode` | `UserController::authByPcode` |
## Пошаговый алгоритм — `POST /user/auth`
1. Тело: `uid`, `regionId`, `email`, `password`, опционально `bdate``UserAuthDto`, валидация.
2. `AuthenticationService::jwtAuth(dto)`:
- `UserRepository::findOneBy(['uid' => $dto->uid])`;
- если пользователь есть — проверка пароля **введённого** `password` через `passwordHasher`;
- если пользователя **нет** — возвращается `user: null` (флаг пароля не применяется).
3. Если пароль не совпал при существующем пользователе — `400`, как при обычном логине.
4. Если пользователя не было — `RegistrationService::create(dto)`:
- `setEmail(md5($dto->email))` (в БД снова **не** хранится открытый email);
- `setUid`, `setRegionId`, роли `ROLE_USER`, `birthDate` из `bdate` (формат `Ymd`), хэш пароля.
5. `updateLoggedIn`, `flush`, выдача JWT через `JWTTokenManagerInterface::create`.
## Пошаговый алгоритм — `POST /user/auth-by-pcode`
1. Тело: `pcode` (кладётся в `UserUidAuthDto::$uid`), `birthDate` или `bdate`.
2. Валидация DTO, разбор даты (`Ymd` или `Y-m-d`).
3. `UserRepository::findOneByUidAndBirthDate($uid, $birthDate)` — сравнение даты **по диапазону суток**.
4. Если не найден — `RegistrationService::createByUidAndBirthDate`:
- `email = md5((string) uid)` — синтетический идентификатор для колонки `email` / JWT `username`;
- регион по умолчанию `1` (аргумент сервиса);
- пароль по умолчанию: `hash( md5(uid . birthDateRaw) )` где первая часть — конкатенация строк из DTO перед нормализацией даты в сущности.
5. `updateLoggedIn`, `flush`, JWT в ответе.
## Mermaid
```mermaid
flowchart TD
A[POST /user/auth или /user/auth-by-pcode] --> V{Валидация DTO}
V -->|ошибка| E400[400 errors]
V -->|ок| B{Какой маршрут?}
B -->|auth| C[jwtAuth по uid]
C --> D{User найден?}
D -->|да| P[проверка password]
P -->|fail| E400b[400 неверный пароль]
P -->|ok| T[JWT + loggedIn]
D -->|нет| R[RegistrationService.create]
R --> T
B -->|auth-by-pcode| F[findOneByUidAndBirthDate]
F -->|есть| T
F -->|нет| G[createByUidAndBirthDate]
G -->|исключение| E500[500]
G -->|ok| T
```
## Внешние зависимости
| Система | Участие |
| --- | --- |
| PostgreSQL | `users` |
| Инфоклиника | **не вызывается напрямую** в этих методах; идентификатор приходит от клиента |
| JWT (Lexik) | выдача токена |
## Обработка ошибок и edge cases
- **Неверный формат даты** в `auth-by-pcode``400` с подсказкой по форматам.
- **Ошибка при создании пользователя** — `500` с текстом исключения (может раскрывать внутренние детали — вопрос hardening).
- **Коллизии `uid`**: на уровне сущности `User` есть ограничение уникальности `uid`; при конфликте БД вернёт ошибку при `flush` (в этом контроллере не разобрана отдельно).
## Ссылки на классы
- `apps/backend/src/Controller/UserController.php` (`auth`, `authByPcode`)
- `apps/backend/src/Service/User/AuthenticationService.php` (`jwtAuth`)
- `apps/backend/src/Service/User/RegistrationService.php` (`create`, `createByUidAndBirthDate`)
- `apps/backend/src/Dto/UserAuthDto.php`, `UserUidAuthDto.php`
- `apps/backend/src/Repository/UserRepository.php` (`findOneByUidAndBirthDate`)
См. [backend-ddd.md](../backend-ddd.md) (контекст Identity).