Repository navigation
Doc Identity
src/Services/Identity/. Отвечает за пользователей: регистрация, вход, выпуск и обновление токенов, выход, профиль и роли. Единственный сервис, который хранит пароли (только хеш BCrypt). База travel_identity.
POST /identity/login
{ "email": "user@example.com", "password": "CHANGE_ME" }{ "accessToken": "eyJ…", "refreshToken": "q8Zk…", "expiresAt": "2026-10-17T09:15:00Z" }Так отвечает сам сервис. Снаружи через Gateway клиент этих токенов не видит: Gateway переносит их в httpOnly cookie и отвечает { "success": true } (см. Вход и сессия).
Внутренний префикс /identity, снаружи /api/v1/auth.
| Метод | Путь | Доступ | Запрос | Ответ |
|---|---|---|---|---|
| POST | /identity/register |
все, лимит auth
|
RegisterRequest(email, password, firstName, lastName, phone?) |
201 AuthTokensDto; 409, если email занят |
| POST | /identity/login |
все, лимит auth
|
LoginRequest(email, password) |
AuthTokensDto; 401 при неверных данных или блокировке |
| POST | /identity/refresh |
все, лимит auth
|
RefreshTokenRequest(refreshToken) |
новая пара токенов; 401, если токен неизвестен, отозван или истёк |
| POST | /identity/logout |
вошедший | RefreshTokenRequest |
204 |
| GET | /identity/me |
вошедший | UserProfileDto(id, email, firstName, lastName, phone?, role, createdAt) |
|
| PATCH | /identity/me |
вошедший | UpdateProfileRequest(firstName?, lastName?, phone?) |
UserProfileDto |
Лимит auth: скользящее окно 60 секунд (6 сегментов), 5 запросов с одного IP (RateLimit:PermitLimit). Работает в дополнение к лимиту Gateway. В docker/docker-compose.yml задано RateLimit__PermitLimit=10000, то есть локально и на dev лимит фактически выключен (задача #32).
-
User:
Id,Email,PasswordHash,FirstName,LastName,Phone,Role,CreatedAt,UpdatedAt. Новый пользователь всегда получает рольClient; ролиManagerиAdminчерез API не выдаются (на dev они есть у демо-аккаунтов сидера). -
RefreshToken:
Token,UserId,ExpiresAt,RevokedAt,ReplacedByToken; активен, если не отозван и не истёк. -
UserRole:
Client,Manager,Admin.
| Токен | Срок | Что внутри |
|---|---|---|
| access (JWT) | 15 минут (JwtSettings:AccessTokenExpirationMinutes) |
id пользователя, email, роль; Issuer = TravelAgency.Identity, Audience = TravelAgency
|
| refresh | 7 дней (JwtSettings:RefreshTokenExpirationDays) |
случайная строка, хранится в базе |
При обновлении старый refresh-токен отзывается и запоминает, каким заменён (ротация): повторно им воспользоваться нельзя.
- После 5 неудачных входов на один email (
LockoutSettings:LockoutThreshold) вход блокируется на 15 минут (LockoutDurationMinutes). Ответ при блокировке тот же 401 «Invalid credentials», чтобы не подсказывать, существует ли аккаунт. - Счётчик хранится в Redis (
RedisLockoutService), если заданаConnectionStrings__Redis; иначе в памяти процесса (InMemoryLockoutService). - Успешный вход сбрасывает счётчик.
Сервер IdentityGrpc на порту 8081: GetUserSummary(user_id) → user_id, email, first_name, last_name, role. Вызывает Booking. См. Межсервисные контракты.
- Персональные данные в логах маскируются (
PiiMaskingDestructuringPolicy). - Ошибки в формате ProblemDetails. Как они пишутся в лог: Логирование ошибок.
- Демо-аккаунты создаёт
IdentityDataSeeder(см. Локальный запуск).
Простой пример. POST /identity/login с неверным паролем: ответ 401 ProblemDetails с detail «Invalid credentials.», в лог одна строка Warning без стектрейса и без записи уровня Error: POST /identity/login 401 UnauthorizedException: Invalid credentials. traceId=.... Строка access-лога для этого запроса уровня Information.
-
LoginCommandHandlerбросаетUnauthorizedException("Invalid credentials.")в трёх случаях: email не найден, пароль не совпал, вход заблокирован. -
IdentityExceptionMapperпереводитAppExceptionв его код:UnauthorizedException401,NotFoundException404,ConflictException409,AppValidationException400. Все эти ответы пишутся как Warning без стектрейса, непредвиденное исключение как 500 и Error со стектрейсом. - Общее правило для всех сервисов: Архитектура.
ConnectionStrings__IdentityDb, ConnectionStrings__Redis (необязательно), JwtSettings__*, LockoutSettings__*, GrpcSettings__InternalServiceToken, Seeding__TestUserPassword. Значения только в окружении.
/health/live, /health/ready.
Система
API
Сервисы
Как реализовано
Справочное
- Видение продукта
- Дорожная карта
- Этап 1: что осталось
- Этап 2: расширение
- Этап 3: масштабирование
- Концепция сайта и интерфейса
- Шаблон ТЗ