Skip to content

Doc Identity

TravelAgency Team Bot edited this page Oct 17, 2026 · 4 revisions

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 } (см. Вход и сессия).

API сервиса

Внутренний префикс /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).
  • Успешный вход сбрасывает счётчик.

gRPC

Сервер 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 в его код: UnauthorizedException 401, NotFoundException 404, ConflictException 409, AppValidationException 400. Все эти ответы пишутся как Warning без стектрейса, непредвиденное исключение как 500 и Error со стектрейсом.
  • Общее правило для всех сервисов: Архитектура.

Настройки

ConnectionStrings__IdentityDb, ConnectionStrings__Redis (необязательно), JwtSettings__*, LockoutSettings__*, GrpcSettings__InternalServiceToken, Seeding__TestUserPassword. Значения только в окружении.

Здоровье

/health/live, /health/ready.

Clone this wiki locally