100 lines
6.2 KiB
Markdown
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).
|