Auth Service
Назначение
auth-service — сервис идентификации и управления пользователями. По коду он отвечает за:
- регистрацию пользователя
- вход по email/паролю
- выпуск access token
- выпуск и rotation refresh token
- получение текущего пользователя
- получение списка пользователей
- обновление пользователя
- деактивацию пользователя
- публикацию доменных событий об изменениях пользователей
Основной транспорт сервиса — RabbitMQ RPC. HTTP-приложение поднимается для Nest runtime, cookie parser, глобального фильтра ошибок и Swagger, но бизнес-операции реализованы RabbitRPC handlers, а не публичными REST endpoint'ами.
Архитектура
Сервис организован слоями.
Application layer
Use case'ы:
RegisterUserUseCaseLoginUserUseCaseRefreshSessionUseCaseGetCurrentUserUseCaseGetUsersUseCaseUpdateUserUseCaseDeactivateUserUseCase
Порты (application/ports):
UserRepositoryRefreshTokenRepositoryPasswordHasherRefreshTokenHasherAccessTokenServiceRefreshTokenService
Domain layer
Содержит:
UserEntityRefreshTokenEntity- value objects:
Email,FullName,PasswordHash,RefreshTokenHash - доменные ошибки (
AuthErrorи конкретные наследники)
Доменные инварианты проверяются именно через value objects и entity-методы, например:
- валидация email
- валидация полного имени
- контроль валидности хэшей
- деактивация пользователя через поведение сущности
Infrastructure layer
Реализации портов:
PrismaUserRepositoryPrismaRefreshTokenRepositoryBcryptPasswordHasherHmacRefreshTokenHasherNestAccessTokenServiceNestRefreshTokenService
Инфраструктурные модули:
PrismaModuleRabbitModule
Модули сервиса
AuthModule
Содержит RabbitRPC handlers для:
- register
- login
- get current user
- refresh session
- deactivate user
Поднимает:
JwtModuleчерезgetJWTConfig()PrismaModuleRabbitModuleRmqEventEmitterModuleсsourceService = auth-serviceи default exchangeefko.auth.events
UserModule
Содержит handlers для:
AuthGetUsersQueryAuthUpdateUserCommand
AppModule
Включает:
ConfigModuleglobalLoggerModule(nestjs-pino)- глобальный
AllExceptionsFilter AuthModuleUserModulePrismaModuleRabbitModule
Команды и queries
RabbitMQ commands
Контроллер AuthController принимает:
AuthRegisterUserCommandAuthLoginUserCommandAuthRefreshSessionCommandAuthDeactivateUserCommand
Контроллер UserController принимает:
AuthUpdateUserCommand
RabbitMQ queries
AuthController:
AuthGetCurrentUserQuery
UserController:
AuthGetUsersQuery
Общие свойства обработчиков:
- очереди
auth-service.commands.queueиauth-service.queries.queue - exchanges
efko.auth.commandsиefko.auth.queries - quorum queues
ValidationPipeна command handlers сtransform,whitelist,forbidNonWhitelisted- корреляция берется из заголовков через
getRpcCorrelationId(...)
Бизнес-поведение по use case'ам
Регистрация
RegisterUserUseCase:
- проверяет уникальность email через
UserRepository.findByEmail - хэширует пароль через
PasswordHasher - создает
UserEntity - сохраняет пользователя в Postgres через Prisma repository
- публикует событие
AuthUserCreatedEventвefko.auth.events
Вход
LoginUserUseCase:
- ищет пользователя по email
- отклоняет вход, если пользователь не найден или деактивирован
- сверяет пароль через
PasswordHasher.compare - генерирует access token и refresh token параллельно
- сохраняет refresh token через
RefreshTokenService.saveRefreshToken
Обновление сессии
RefreshSessionUseCase:
- вычисляет HMAC/hash от входящего refresh token
- ищет сессию по хэшу
- отклоняет revoked/expired/not found токены
- повторно проверяет активность пользователя
- удаляет старую запись refresh token
- генерирует новую пару токенов
- сохраняет новый refresh token
Это rotation-модель: refresh token одноразовый, старая запись удаляется при успешном refresh.
Текущий пользователь
GetCurrentUserUseCase:
- читает пользователя по
userId - возвращает только активного пользователя
- деактивированный пользователь трактуется как
USER_NOT_FOUND
Список пользователей
GetUsersUseCase:
- читает всех пользователей через
findAll() - маппит сущности в контрактную схему
Обновление пользователя
UpdateUserUseCase:
- загружает текущую сущность
- пересобирает
UserEntityчерезhydrate(...) - применяет частичное обновление email, fullName, role, employeeId
- сохраняет изменения через
UserRepository.update
Деактивация
DeactivateUserUseCase:
- загружает пользователя
- деактивирует сущность
- параллельно:
- обновляет пользователя
- удаляет все refresh token пользователя
- публикует событие
AuthUserDeactivatedEvent
Хранение данных
Сервис использует PostgreSQL через Prisma.
Таблица users
Поля по schema.prisma:
idUUIDemailuniquepassword_hashfull_nameroleis_activeemployee_idnullablecreated_atupdated_at
Роли хранятся как enum user_role:
adminmanageranalystshift_manageremployee
Таблица refresh_tokens
Поля:
idUUIDuser_idtoken_hashexpires_atis_revokedcreated_at
Связи и индексы:
RefreshToken.userId -> User.idonDelete: Cascade- индексы по
userIdиexpiresAt
Что именно хранится
- access token в БД не хранится
- refresh token хранится только в виде hash
- пароль хранится только в виде bcrypt hash
Интеграции
RabbitMQ
getRMQConfig() объявляет:
- exchanges:
efko.auth.commandsefko.auth.queriesefko.auth.events- queues:
auth-service.commands.queueauth-service.queries.queueauth-service.events.queue
Настройки:
- quorum queues
prefetchCount = 32persistentpublishconnectionInitOptions.wait = false
JWT
NestAccessTokenService использует JwtService.signAsync/verifyAsync. Конфигурация берется из:
JWT_ACCESS_SECRETJWT_ACCESS_TTLJWT_ACCESS_ISSUER
Payload access token по коду включает:
subemailrole
Хэширование и криптография
- password hashing вынесен в
BcryptPasswordHasher - refresh token генерируется как
crypto.randomBytes(32).toString('base64url') - перед сохранением refresh token хэшируется через
RefreshTokenHasher - TTL refresh token вычисляется из
JWT_REFRESH_TTL
Внешние контракты и события
Сервис зависит от общих контрактов в @efko-kernel/contracts и @efko-kernel/interfaces.
Публикуемые события:
AuthUserCreatedEventAuthUserDeactivatedEvent
Обработка ошибок
Доменные ошибки
AUTH_ERROR_HTTP_STATUS_MAP задает соответствия:
INVALID_EMAIL->400INVALID_FULL_NAME->400USER_ALREADY_EXISTS->409USER_NOT_FOUND->404INVALID_CREDENTIALS->401USER_ALREADY_DEACTIVATED->409REFRESH_TOKEN_REVOKED->401REFRESH_TOKEN_EXPIRED->401INVALID_REFRESH_TOKEN->401
RPC-ошибки
authRpcErrorInterceptor преобразует AuthError и HttpException в RpcErrorResponse с полями:
error.codeerror.messageerror.statusCode
Это позволяет gateway и другим клиентам стабильно маппить ошибки в HTTP/consumer semantics.
HTTP-ошибки
AllExceptionsFilter:
- в HTTP-контексте возвращает JSON с
statusCodeиmessage - в non-HTTP контексте формирует
RpcErrorResponse - неизвестные исключения отдает как internal error через базовый filter
Observability и logging
nestjs-pinoиспользуется как основной logger- в dev-режиме пишется
logs/auth-service.log - все use case'ы логируют ключевые шаги через
Logger - в лог-контекст добавляется
correlationIdи доменные атрибуты черезbuildLogContext(...) - Swagger поднимается на
/api, JSON —/api/swagger/json
Логируются, в частности:
- попытки регистрации и логина
- поиск пользователя/сессии
- генерация и сохранение токенов
- деактивация пользователя
RabbitMQ Commands и Queries
Commands
Все команды отправляются в exchange efko.auth.commands, очередь auth-service.commands.queue.
AuthRegisterUserCommand
Регистрация нового пользователя.
Exchange: efko.auth.commands
Routing Key: auth.register.command
Queue: auth-service.commands.queue
Request Body:
{
email: string; // Email пользователя, должен быть уникальным
password: string; // Пароль (минимум 8 символов)
fullName: string; // Полное имя (формат: Фамилия Имя Отчество)
role?: UserRole; // Роль (опционально, по умолчанию EMPLOYEE)
employeeId?: string; // UUID сотрудника (опционально)
}
Response:
{
id: string; // UUID пользователя
email: string; // Email
fullName: string; // Полное имя
role: UserRole; // Роль
isActive: boolean; // Статус активности
employeeId: string | null; // ID сотрудника (если привязан)
}
Возможные ошибки:
INVALID_EMAIL->400INVALID_FULL_NAME->400USER_ALREADY_EXISTS->409
AuthLoginUserCommand
Вход в систему по email/пароль.
Exchange: efko.auth.commands
Routing Key: auth.login.command
Queue: auth-service.commands.queue
Request Body:
Response:
{
accessToken: string; // JWT access токен
refreshToken: string; // Refresh токен
user: {
id: string;
email: string;
fullName: string;
role: UserRole;
isActive: boolean;
employeeId: string | null;
};
}
Возможные ошибки:
USER_NOT_FOUND->404INVALID_CREDENTIALS->401USER_ALREADY_DEACTIVATED->409
AuthRefreshSessionCommand
Обновление сессии по refresh токену.
Exchange: efko.auth.commands
Routing Key: auth.refresh-session.command
Queue: auth-service.commands.queue
Request Body:
Response:
{
accessToken: string;
refreshToken: string; // Новый refresh токен (rotation)
user: {
id: string;
email: string;
fullName: string;
role: UserRole;
isActive: boolean;
employeeId: string | null;
};
}
Возможные ошибки:
REFRESH_TOKEN_REVOKED->401REFRESH_TOKEN_EXPIRED->401INVALID_REFRESH_TOKEN->401
AuthUpdateUserCommand
Обновить данные пользователя.
Exchange: efko.auth.commands
Routing Key: auth.update-user.command
Queue: auth-service.commands.queue
Request Body:
{
userId: string; // UUID пользователя для обновления
email?: string; // Новый email (должен быть уникальным)
fullName?: string; // Новое полное имя
role?: UserRole; // Новая роль
employeeId?: string | null; // ID сотрудника или null
}
Response:
{
id: string;
email: string;
fullName: string;
role: UserRole;
isActive: boolean;
employeeId: string | null;
}
Возможные ошибки:
USER_NOT_FOUND->404INVALID_EMAIL->400INVALID_FULL_NAME->400
AuthDeactivateUserCommand
Деактивировать пользователя.
Exchange: efko.auth.commands
Routing Key: auth.deactivate-user.command
Queue: auth-service.commands.queue
Request Body:
Response:
Возможные ошибки:
USER_NOT_FOUND->404USER_ALREADY_DEACTIVATED->409
Queries
Все запросы отправляются в exchange efko.auth.queries, очередь auth-service.queries.queue.
AuthGetCurrentUserQuery
Получить профиль пользователя по JWT токену.
Exchange: efko.auth.queries
Routing Key: auth.get-current-user.query
Queue: auth-service.queries.queue
Request Body:
Response:
{
id: string;
email: string;
fullName: string;
role: UserRole;
isActive: boolean;
employeeId: string | null;
}
Возможные ошибки:
USER_NOT_FOUND->404
AuthGetUsersQuery
Получить список всех пользователей.
Exchange: efko.auth.queries
Routing Key: auth.get-users.query
Queue: auth-service.queries.queue
Request Body:
Response:
{
users: Array<{
id: string;
email: string;
fullName: string;
role: UserRole;
isActive: boolean;
employeeId: string | null;
}>;
}
Встраивание в систему
auth-service — источник истины для идентичности пользователя в системе:
gatewayиспользует его как resolver профиля по JWTsub- административные операции по пользователям тоже проходят через него
- другие сервисы могут подписываться на
efko.auth.events employeeIdсвязывает auth-пользователя с кадровым контуром
Допущения и пробелы по коду
- В сервисе нет отдельного health/readiness endpoint.
- В
schema.prismadatasource не содержит URL; он ожидается из runtime-конфигурации Prisma/Nest окружения. - HTTP-приложение поднимается, но в коде нет явного публичного REST API для бизнес-операций; основной интерфейс сервиса — RabbitMQ RPC.