Парные резолверы registerByPhone/registerByEmail и аналогичные им удалены
полностью и заменены единым контрактом: register/confirmRegister/login/
requestPasswordRecovery/confirmPasswordRecovery/verificationPeek.
Канал (PHONE/EMAIL) определяется бэкендом по формату поля identity.
Ядро решения — непрозрачный подписанный JWT (JWT_EMAIL_CONFIRM_KEY), ничего не хранящий
на сервере. Для email-канала это тот же артефакт, что раньше назывался token (ссылка в
письме); для phone-канала — новый JWT, оборачивающий номер телефона. Сам SMS-код по-прежнему хранится
в Redis notify-service по номеру телефона — это не менялось. Claims: purpose,
channel, ровно одно из email/phone, опционально
name/redirect/userId, iat/exp.
| Было (2 резолвера) | Стало (1 резолвер) | Канал определяет |
|---|---|---|
registerByPhone / registerByEmail | register | identity |
confirmRegisterByPhone / ByEmail | confirmRegister | verificationId |
loginByPhone / loginByEmail | login | identity |
requestPasswordRecoveryBy* | requestPasswordRecovery | identity |
confirmPasswordRecoveryBy* | confirmPasswordRecovery | verificationId |
phoneCodeIsValid + emailTokenGetState | verificationPeek | verificationId |
Старые резолверы удалены полностью, без deprecated-обёрток — стиль проекта: чистый рефакторинг, не shim'ы.
Три новых публичных метода, построенных поверх существующих sendPhoneCode/confirmPhoneCode/
peekPhoneCode/confirmEmailLink:
startVerification(purpose, {identity, name?, redirect?}) → {verificationId, channel}confirmVerification(allowed: VerificationPurpose[], {verificationId, code?}) → {valid, user?, redirect?, channel}peekVerification(verificationId, code?) → {valid, purpose?, name?, channel?}enums/verification-channel.enum.ts — VerificationChannel { EMAIL, PHONE }utils/identity.util.ts — detectIdentityChannel(value)decorators/is-identity.decorator.ts — @IsIdentity()dto/common/identity.dto.ts, dto/common/verification-id.dto.tsresolvers/auth/{register,login,recovery-pass,verification-peek}.resolver.tsphone-code.resolver.ts, email-token.resolver.ts, парные DTO под register/login/recovery/phone-codeshared/src/api/user/{register,confirm-register,login,verification-peek}.js, recovery-password/{request,confirm}.jsregister.vue, login.vue (front + admin), recovery.vue (только front)confirm/by-email.vue: проп token → verificationIdAppEmailOrPhoneField (apps/shared/src/ui/form/email-phone.vue) не менялся — маска ввода, не выбор мутацииSMS-коды в Redis notify-service хранятся по номеру телефона без TTL и без purpose-метки — код от REGISTER теоретически может подтвердить RECOVER для того же номера в окне до перезаписи. Не исправлено сейчас (notify-service вне рамок), зафиксировано как рекомендация на будущее.
OAuth (соцвход) намеренно не встроен в verificationId/VerificationChannel —
это закрытый union EMAIL|PHONE. Под соцвход зарезервирована отдельная будущая мутация
loginWithProvider(provider, authorizationCode, redirect), переиспользующая только
loginAndSetToken.
secured-fields.resolver.ts (смена телефона/почты уже авторизованным пользователем,
requestPhoneChange/confirmPhoneChange/requestEmailChange/changeMyPass)
не переделывался — публичные методы VerificationService, которыми он пользуется,
сохранили сигнатуры.