Направление работ: AdmLk
Код раздела: 1.1
Раздел: Вход
Статус: черновик
Ответственный: Панкова Л.А. Версия: 0.1
Обновлено: 15.07.26


1. Назначение

Раздел описывает вход пользователя в сервисную зону МАРТ по email или телефону и паролю.

Интерфейс должен проверить учетные данные, показать ошибку при неуспешном входе или открыть сервисную зону при наличии доступа.

2. Ключевые понятия

Вход в сервисную зону — сценарий доступа пользователя в AdmLk по учетным данным аккаунта.

Учетные данные — email или телефон и пароль пользователя.

Сервисная зона — зона управления системой и глобальными настройками.

3. Scope

Входит

S1. Форма входа.

S2. Отправка учетных данных.

S3. Ошибки входа и отсутствия доступа.

Не входит

  • Back 1.1.2 — вход в систему.
  • Back 1.2 — проверка доступа к сервисной зоне.
  • AdmLk 1.2 — главная страница после входа.
  • AdmLk 3.7 — сотрудники и доступ к сервисной зоне.

4. BR

Указаны в Back 1.1.2 «Вход в систему» и Back 1.2 «Организация доступа к системе».

5. Функциональные требования

IDСценарийФункциональное требованиеBRВесСтатус
FR1.1-1S1. Пользователь открывает входИнтерфейс показывает поля «Email или телефон» и «Пароль», а также кнопку «Продолжить».1Реализовано
FR1.1-2S1. Пользователь заполняет формуИнтерфейс принимает email или телефон, маскирует пароль и позволяет показать или скрыть пароль.1Реализовано
FR1.1-3S2. Пользователь отправляет данныеИнтерфейс активирует «Продолжить» после заполнения обязательных полей и отправляет учетные данные на проверку.1Реализовано
FR1.1-4S2. Вход выполнен успешноИнтерфейс открывает сервисную зону, если учетные данные верны и у пользователя есть доступ.1Реализовано
FR1.1-5S3. Данные неверныеИнтерфейс показывает ошибку «Неверный логин или пароль» и позволяет исправить учетные данные.1Реализовано
FR1.1-6S3. Доступ отсутствуетИнтерфейс показывает ошибку «Вход не выполнен. Отсутствует доступ» и кнопку «Войти в другой аккаунт».1Частично \ #OQ1.1-2, #OQ1.1-10
FR1.1-7S3. Пользователь выбирает другой аккаунтИнтерфейс возвращает пользователя к форме входа для ввода других учетных данных.2Не реализовано \ #OQ1.1-3

6. Открытые вопросы

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
#OQ1.1-1Figma / Back \ Код вне BRDДолжен ли AdmLk поддерживать восстановление пароля со страницы входа или сценарий вынесен за рамки текущего раздела? По коду: уже реализовано — на форме есть кнопка «Забыл пароль» → /auth/password-recovery, присутствует полный сценарий восстановления. \ LoginForm.vue:51-58; features/auth-password-recovery; AuthService.ts:85-112Бизнес (Герман)OpenВопрос из стадии A снят наполовину: код отвечает «поддерживается». Осталось продуктовое решение. (А, рекоменд.) закрыть вопрос как «поддерживается» и добавить требование на «Забыл пароль» в scope 1.1; (Б) если восстановление не должно жить в сервисном кабинете — скрыть кнопку и явно вынести за скоуп.Артем: по коду еще нормально не реализовано. Весь раздел в любом случае нужно будет переписывать, перенося наработки Кости. Восстановление пароля я бы оставил.
#OQ1.1-2Код FR1.1-6Требование говорит: при отсутствии доступа показать «Вход не выполнен. Отсутствует доступ» и кнопку «Войти в другой аккаунт». В коде сценария нет вообще: при успехе всегда редирект на /, при любой ошибке — только «Неверный логин или пароль»; состояние «нет доступа» не различается. \ useAuthLogin.ts:16-33 (поиск по «Отсутствует доступ»/«Войти в другой» — совпадений нет)Бизнес/РазработкаOpenТребование веса 1 не реализовано; «нет доступа» неотличим от «неверный пароль». Механика упирается в контракт: согласовать с Back: 1.2, возвращает ли бэкенд отдельный код отказа по доступу. (А, рекоменд.) бэкенд даёт код отказа → фронт показывает экран «Отсутствует доступ» + «Войти в другой аккаунт»; (Б) если отказ по доступу на этом этапе невозможен — зафиксировать в BRD, что FR1.1-6/FR1.1-7 вне текущей реализации, и снять вес 1. Связать с OQ1.1-3, OQ1.1-5.Артем: весь вход был сделан еще до моего прихода, его в любом случае переписывать, так что я бы на него не ориентировался. Зачем нужна отдельная кнопка “Войти в другой аккаунт” я не понимаю - по сути мы еще ни в один не вошли. Выглядит, как лишний отвлекающий визуальный мусор. Я бы оставил только предупреждение “Вход не выполнен. Отсутствует доступ”
#OQ1.1-3Код FR1.1-7Требование говорит: «Войти в другой аккаунт» возвращает пользователя к форме входа. В коде кнопки и перехода нет — зависит от отсутствующего экрана отказа (см. OQ1.1-2); отдельный механизм выхода существует, но с экраном отказа не связан. \ features/auth-logout/model (поиск по «Войти в другой» — совпадений нет)Бизнес/РазработкаOpenПроизводная от OQ1.1-2 — без экрана отказа кнопке негде жить. Рекомендация: при реализации переиспользовать features/auth-logout (кнопка = выход + возврат к форме). Решается вместе с OQ1.1-2.Артем: см. предыдущий пункт. Если сделать отдельное отображение кнопки, которое будет возвращать обратно, то это абсолютно бессмысленное принуждение пользователя кликать на кнопку после каждой неудачной попытки. По мне, сообщения с ошибкой достаточно.
#OQ1.1-4Код вне BRDЧто нашли: на форме входа есть кнопка «Регистрация» → /auth/registration, а весь вход построен на партнёрском сценарии (эндпоинт LoginPartner). В BRD 1.1 саморегистрация не описана. \ LoginForm.vue:42-49; server/api/auth/login.post.ts:25Бизнес (Герман)OpenЭкран входа переиспользован из партнёрского кабинета. Решение бизнеса: нужна ли кнопка «Регистрация» в сервисной зоне (доступ туда выдаётся через AdmLk 3.7 / Back 8.1, а не самозаписью). (А, рекоменд.) убрать кнопку и вынести саморегистрацию за скоуп; (Б) если нужна — внести требованием. Контракт партнёрского входа — отдельный вопрос OQ1.1-9.Артем: реализация раздела черновая. Считаю, что кнопку стоит убрать.
#OQ1.1-5Код FR1.1-5Требование говорит: показать «Неверный логин или пароль» и дать исправить данные. В коде текст верен для неверных данных, но захардкожен на ЛЮБУЮ ошибку: сервер маппит все не-OK ответы в это сообщение, а форма игнорирует реальный текст ошибки — сеть, сбой сервера и «нет доступа» тоже показываются как «Неверный логин или пароль». \ LoginForm.vue:79-84; server/api/auth/login.post.ts:39-46РазработкаЗакрывает PMХардкод текста на любую ошибку — дефект, продуктового выбора нет (какой текст для отказа по доступу — решается в OQ1.1-2). Закрываю сам: пробрасывать реальный код/текст ошибки от бэкенда, «Неверный логин или пароль» показывать только для invalid-credentials, для сети/сервера — отдельный текст → тикет в Bitrix. Это же открывает механику для OQ1.1-2.Артем: реализация раздела черновая. Нужно переделать
#OQ1.1-6Код FR1.1-4Требование говорит: после верного входа открыть сервисную зону. В коде редирект на / есть, но auth_token ставится httpOnly, а проверки isAuthenticated/isTokenValid читают его через document.cookie — на клиенте всегда false, из-за чего глобальный middleware дёргает refresh-token на каждом переходе. \ login.post.ts:58,64; account.session.ts:21-27; AuthService.ts:159-163; auth.global.ts:34-49Разработка (Влад)OpenВход работает, но механика сессии на клиенте сломана — лишний refresh на каждом переходе. Выбор архитектурный. Вопрос к Владу: (А, рекоменд.) определять авторизацию по состоянию сессии / ответу /api/auth/account, а не по httpOnly-куке (её из JS не видно), либо добавить лёгкую не-httpOnly куку-флаг; (Б) оставить как есть, приняв refresh на каждом переходе как цену (хуже по производительности).Артем: реализация раздела черновая. Нужно переделать
#OQ1.1-7Код вне BRDЧто нашли: две точки входа на одну форму — /auth/authorization и /login (через отдельный widget на тот же LoginForm); в middleware публичен только /auth/authorization, а /login — нет, поэтому неавторизованного с /login редиректит прочь. Плюс два почти одинаковых middleware. \ pages/login.vue:1-7; widgets/auth-login/ui/AuthLoginWidget.vue:1-8; auth.global.ts:20-29; middleware/auth.ts:20-25РазработкаЗакрывает PMДубль маршрута с нерабочим поведением (/login недоступен неавторизованному — то есть тем, кому он и нужен), продуктового вопроса нет. Закрываю сам: оставить один маршрут /auth/authorization, удалить /login + widget (или сделать редирект), свести два middleware к одному → тикет в Bitrix.Артем: реализация раздела черновая. Нужно переделать
#OQ1.1-8Код FR1.1-3Требование говорит: активировать «Продолжить» после заполнения обязательных полей. В коде кнопка активна не по «заполненности», а по валидному формату email/телефона и паролю ≥ 4 символов; порог 4 расходится с PASSWORD_MIN_LENGTH=8 и схемой min(1) — три разных значения в одном проекте. \ LoginForm.vue:87-112; error-messages.ts:18; auth.schema.ts:28РазработкаЗакрывает PMГейт строже требования + рассинхрон порогов (4 / 8 / 1). Длину пароля на входе проверяет бэкенд, навязывать её форме незачем. Закрываю сам: гейтить кнопку по непустым полям, длину/сложность на входе не проверять; пороги, если нужны, вынести в конфиг и устранить рассинхрон → тикет в Bitrix.Артем: реализация раздела черновая. Нужно переделать
#OQ1.1-9Код вне BRDЧто нашли: вход сервисной зоны идёт через IdentityService/LoginPartner, тело запроса — fullPhoneNumber. То есть сервисный кабинет логинится партнёрским методом. \ server/api/auth/login.post.ts:20-25; AuthService.ts:16-24Разработка (Влад)OpenВопрос к Владу + Back: 1.1.2: верный ли это метод/контракт identity для сервисного кабинета. (А) подтвердить, что LoginPartner применим и для сервисной зоны; (Б) завести отдельный service-login контракт. Связано с OQ1.1-4 (экран переиспользован из партнёрского кабинета) и с OQ1.1-2 (где живёт отказ по доступу).Артем: считаю, что лучше добавить сервисный метод
#OQ1.1-10Код FR1.1-6 (дельта-сверка 2026-09-08)Требование говорит: при отсутствии доступа показать «Вход не выполнен. Отсутствует доступ». В коде текст сообщения теперь существует и корректен (AUTH_ACCESS_DENIED), но код-триггер для типового случая «аккаунт не сотрудник сервиса» совпадает с кодом «неверный пароль»: оба пути (строка 127 — неверный пароль, и ветка employee is null | employee.IsArchived в EnsureServiceAdminAccessAsync) бросают один и тот же AuthorizationException (ExceptionType.Authorization) для метода LoginService, что по каталогу кодов даёт один и тот же numeric-код 4010361. Фронт распознаёт 4010361 как invalid_credentials, а не access_denied — отличимый код access_denied (4011361) получает только более редкий путь: заархивированная/удалённая ABAC-роль (InvalidRoleException). То есть для основного сценария «нет доступа» пользователь увидит «Неверный логин или пароль» вместо требуемого текста. \ booking-monorepo: src/services/booking.authentication/src/Authentication.Application/Accounts/Commands/Internal/Login/LoginInternalCommandHandler.cs:127,245-263; src/services/booking.authentication/src/Authentication.Presentation/Messages/AuthMethodMessageCatalog.cs:12,22,47; src/services/booking.authentication/src/Authentication.Presentation/Messages/AuthUserMessageFactory.cs:154-166 \ musbooking.weblk.adminpanel: shared/api-contracts/auth/auth.schema.ts:39,45-51,58РазработкаOpenСтатически коды разведены неверно: EnsureServiceAdminAccessAsync (LoginInternalCommandHandler.cs:245-263, ветка «employee отсутствует/заархивирован») должна бросать код, отличимый от «неверный пароль» (например, InvalidRoleException, как соседняя ветка «роль отсутствует», → код 4011361), либо завести отдельный код именно под этот случай и обновить ACCESS_DENIED_META_CODES/INVALID_CREDENTIALS_META_CODES на фронте. Вывод основан на статическом чтении catalog/factory, реальный проход через стенд не делали — перед фиксом стоит подтвердить фактический meta.message.code в ответе на реальный «нет доступа»-логин.
#OQ1.1-11Код вне BRD / связано с OQ1.1-1В OQ1.1-1 итоговое решение — «восстановление пароля я бы оставил» (Артем). Но более ранний техдолг-документ docs/technical debt/Вход.md (актуален на 30.07.2026, удалён коммитом 9fe1ebc «1.1 Вход» от 03.09.2026) фиксировал обратное: «Регистрация / восстановление пароля в AdmLk — вне скоупа — не реализовывать и не оставлять UI». По факту в текущем коде кнопок «Забыл пароль» и «Регистрация» нет вообще, страниц /auth/password-recovery и /auth/registration не существует — реализация соответствует техдолг-документу, а не итоговому решению из OQ1.1-1. \ musbooking.weblk.adminpanel: pages/auth (единственная страница — authorization.vue); grep по «Забыл пароль» / «password-recovery» / «Регистрация» / «registration» по *.vue, *.ts — совпадений нет; коммит 9fe1ebc (Kondratev Artyom, 2026-09-03) удаляет docs/technical debt/Вход.md и docs/Флоу авторизации.mdБизнесOpenЗафиксировать одно решение в BRD: (А) восстановление пароля возвращается в скоуп AdmLk 1.1 и его нужно реализовать заново (сейчас в коде отсутствует полностью); (Б) подтвердить, что оно вне скоупа — тогда текущее состояние кода уже соответствует ожиданиям и OQ1.1-1 можно закрыть. Саморегистрация (OQ1.1-4) под тем же документом уже согласована как «убрать» — код это подтверждает, здесь конфликта нет.

Вопросы по сверке с кодом

BRD 1.1 v0.1 · musbooking.weblk.adminpanel · commit c376579/main · 2026-07-10 · якорь features/auth-login (+ pages/auth/authorization.vue, entities/account, shared/api/AuthService.ts, server/api/auth) Сводка: вопросов 9 (бизнесовых 4 / программных 5) · подтверждено без вопросов 2 (FR1.1-1, FR1.1-2) · отрезано находок обратного прохода 4 (фильтр кириллицы/пробелов в пароле, дропдаун кодов стран и международный формат телефона, выход из системы, перехватчик обновления токена) Под особым вниманием: FR1.1-6 / FR1.1-7 — сценарий «отсутствует доступ» в коде не реализован; FR1.1-4 — хранение токена/сессии на клиенте работает не так, как ожидается. Повторная сверка (дельта) по итогам дейликов · musbooking.weblk.adminpanel commit 3a107bd/main (2026-09-06) · booking-monorepo commit 8235929/main (2026-09-07, кросс-слой: Authentication.Application, Authentication.Presentation) · дата сверки 2026-09-08 · якорь расширен: features/auth-login (структура изменилась — AuthLoginForm.vue, model/login.ts, model/schema.ts, model/loginErrors.ts), pages/auth/authorization.vue, widgets/auth-login, entities/account/store/{account.session,account.api}.ts, shared/api/AuthService.ts, shared/api-contracts/auth/auth.schema.ts, shared/config/error-messages.ts, middleware/auth.global.ts, server/api/auth. Раздел переписан коммитом 9fe1ebc «1.1 Вход» (Kondratev Artyom, 2026-09-03). Итог дельты: статусы обновлены у 5 из 7 FR (FR1.1-3, FR1.1-4, FR1.1-5 → Реализовано; FR1.1-6 → Частично; FR1.1-7 без изменений), добавлено 2 новых открытых вопроса (OQ1.1-10 — Разработке, OQ1.1-11 — Бизнесу), у 5 существующих OQ (OQ1.1-4, 5, 6, 7, 8, 9) код больше не показывает исходную проблему — подробности и признак «выглядит закрытым» см. 1.1-admlk.changes.md; сами строки таблицы выше не редактировались. Гайдбук не передавался — маршрутизация без сверки с гайдбуком.

Бизнесовые

№Связано сЧто в BRDЧто в кодеГде в коде
Б1FR1.1-6При отсутствии доступа показать «Вход не выполнен. Отсутствует доступ» и кнопку «Войти в другой аккаунт»Сценария нет: при успехе всегда редирект на /, при любой ошибке — только «Неверный логин или пароль»; состояние «нет доступа» не различается — нужно решение по UX отказа в доступеuseAuthLogin.ts:16-33; grep по «Отсутствует доступ»/«Войти в другой» — совпадений нет
Б2FR1.1-7«Войти в другой аккаунт» возвращает пользователя к форме входаКнопки и перехода нет (зависит от Б1); отдельный механизм выхода есть, но с экраном отказа не связанfeatures/auth-logout/model; grep по «Войти в другой» — совпадений нет
Б3OQ1.1-1Открытый вопрос: поддерживать ли восстановление пароля со страницы входаУже реализовано: на форме есть кнопка «Забыл пароль» → /auth/password-recovery, присутствует полный сценарий восстановления — нужно решение, оставляем ли в скоупе AdmLk 1.1LoginForm.vue:51-58; features/auth-password-recovery; AuthService.ts:85-112
Б4вне BRD—На форме входа есть кнопка «Регистрация» → /auth/registration, а весь вход построен на партнёрском сценарии (эндпоинт LoginPartner) — нужно решение, входит ли саморегистрация / партнёрский вход в сервисный кабинетLoginForm.vue:42-49; server/api/auth/login.post.ts:25

Рекомендация / варианты решения

  • Сценарий отсутствия доступа (Б1, Б2; механика — П1) — сейчас «нет доступа» неотличим от «неверный пароль». Вариант А (рекоменд.): бэкенд (Back 1.2) возвращает отдельный код отказа, фронт показывает экран «Отсутствует доступ» + «Войти в другой аккаунт» (кнопка = выход + возврат к форме, переиспользовать features/auth-logout). Вариант Б: если для сервисного кабинета отказ по доступу на этом этапе невозможен — зафиксировать в BRD, что FR1.1-6 / FR1.1-7 вне текущей реализации, и снять вес 1.
  • Восстановление пароля (Б3) — код уже поддерживает. Вариант А (рекоменд.): закрыть OQ1.1-1 как «поддерживается» и добавить требование на «Забыл пароль». Вариант Б: если восстановление не должно жить в сервисном кабинете — скрыть кнопку и явно вынести за скоуп.
  • Саморегистрация и партнёрский вход (Б4; контракт — П5) — экран входа переиспользован из партнёрского кабинета. Согласовать с продуктом: нужна ли кнопка «Регистрация» в сервисной зоне и корректен ли партнёрский identity-флоу; при отказе — убрать кнопку и уточнить контракт входа.

Программные

№Связано сЧто в BRDЧто в кодеГде в коде
П1FR1.1-5Показать «Неверный логин или пароль» и дать исправить данныеТекст верен для неверных данных, но захардкожен на ЛЮБУЮ ошибку: сервер маппит все не-OK ответы в это сообщение, а форма игнорирует реальный текст ошибки — сеть/сервер/«нет доступа» тоже показываются как «Неверный логин или пароль»LoginForm.vue:79-84; server/api/auth/login.post.ts:39-46
П2FR1.1-4После верного входа открыть сервисную зонуРедирект на / есть, но auth_token ставится httpOnly, а isAuthenticated / isTokenValid читают его через document.cookie — на клиенте всегда false, из-за чего глобальный middleware дёргает refresh-token на каждом переходеlogin.post.ts:58,64; account.session.ts:21-27; AuthService.ts:159-163; auth.global.ts:34-49
П3вне BRD—Две точки входа на одну форму: /auth/authorization (pages/auth) и /login (pages/login.vue → widgets/auth-login → тот же LoginForm); в middleware публичен только /auth/authorization, /login — нет, неавторизованного с /login редиректит прочь. Плюс два почти одинаковых middlewarepages/login.vue:1-7; widgets/auth-login/ui/AuthLoginWidget.vue:1-8; auth.global.ts:20-29; middleware/auth.ts:20-25
П4FR1.1-3Активировать «Продолжить» после заполнения обязательных полейКнопка активна не по «заполненности», а по валидному формату email/телефона и паролю ≥ 4 символов; порог 4 расходится с PASSWORD_MIN_LENGTH=8 и схемой min(1)LoginForm.vue:87-112; error-messages.ts:18; auth.schema.ts:28
П5вне BRD—Вход сервисной зоны идёт через IdentityService/LoginPartner, тело запроса — fullPhoneNumber; подтвердить, что это верный метод/контракт identity для сервисного кабинета (Back 1.1.2)server/api/auth/login.post.ts:20-25; AuthService.ts:16-24

Рекомендация / варианты решения

  • Разделение ошибок входа (П1; закрывает механику Б1) — Вариант А (рекоменд.): пробрасывать реальный код/текст ошибки от бэкенда и показывать «Неверный логин или пароль» только для invalid-credentials, «Отсутствует доступ» — для отказа по доступу, отдельный текст — для сети/сервера. Вариант Б (минимум): не хардкодить текст, а показывать фактический session.error.
  • Хранение токена/сессии на клиенте (П2) — Вариант А (рекоменд.): определять авторизацию по состоянию сессии / ответу /api/auth/account, а не по httpOnly-куке (её из JS не видно) — либо добавить лёгкую не-httpOnly куку-флаг. Вариант Б: оставить как есть, осознанно приняв refresh на каждом переходе как цену (хуже по производительности).
  • Дубли точек входа и middleware (П3) — Вариант А (рекоменд.): оставить один маршрут (/auth/authorization), удалить /login + widget или сделать из него редирект, свести два middleware к одному. Вариант Б: если /login нужен — добавить его в список публичных маршрутов.
  • Порог валидации кнопки (П4) — логину лучше не навязывать длину пароля (её проверяет бэкенд). Вариант А (рекоменд.): гейтить кнопку по непустым полям, длину/сложность на входе не проверять. Вариант Б: если формат-валидация нужна — вынести пороги в конфиг и устранить рассинхрон 4 / 8 / 1.
  • Контракт метода входа (П5) — подтвердить с бэкендом (Back 1.1.2), что LoginPartner — правильный метод для сервисной зоны; при необходимости завести отдельный service-login контракт.

0 items under this folder.