Направление работ: Mobile Код раздела: 1.3 Авторизация > Вход Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 14.07.26


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

Раздел описывает вход пользователя в мобильное приложение по телефону или email и паролю.

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

Документ предназначен для проектирования макета и фронтового сценария. Он не задает backend-логику аутентификации.

Раздел зависит от Back: 1.1 Организация аккаунтов и Back: 1.1.2 Вход в систему.

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

Вход — пользовательский сценарий авторизации в мобильном приложении.

Способ входа — выбор идентификатора: телефон или email.

Идентификатор — телефон или подтвержденный email, по которому пользователь входит в аккаунт.

Ошибка входа — состояние, при котором пользователь остается на экране входа и может исправить данные и повторить действие.

3. Scope

Входит

  • Открытие экрана входа
  • Выбор способа входа: телефон или почта
  • Ввод идентификатора и пароля
  • Отправка данных для входа
  • Состояния входа
  • Переход к регистрации или к восстановлению пароля и следующему сценарию

Не входит

Mobile: 1.2 Регистрация

Mobile: 1.4 Восстановление пароля

Mobile: 1.3.1 Вход по код-паролю или биометрии

4. BR

Бизнес-правила и ограничения указаны в Back: 1.1 Организация аккаунтов и Back: 1.1.2 Вход в систему.

В этом документе связи с BR указаны только при прямом соответствии фронтового требования backend-правилу.

5. Функциональные требования (UC + FR)

FR – functional requirements, что интерфейс должен уметь делать по определенному UC (триггеру).

IDСценарийФункциональное требованиеBRВесСтатус
FR-1Пользователь открывает приложение без активизированной сессииИнтерфейс показывает экран входа при запуске приложения.Back: 1.1 Back: 1.1.21Частично \ #OQ1.3-6
FR-2Пользователь выбирает способ входаИнтерфейс позволяет выбрать вход по телефону или по почте. При выборе способа меняется поле идентификатора и доступный тип клавиатуры.Back: 1.1.21Реализовано
FR-3Пользователь вводит идентификатор и парольИнтерфейс принимает телефон или email и пароль, применяет соответствующую маску/формат поля, скрывает пароль по умолчанию и позволяет показать пароль. Длина телефона ограничивается 10 цифрами.Back: 1.1 Back: 1.1.21Частично \ #OQ1.3-1 \ #OQ1.3-10
FR-4Пользователь отправляет данные для входаИнтерфейс активирует продолжение после заполнения обязательных полей и отправляет выбранный идентификатор вместе с паролем на проверку наличия Аккаунта.Back: 1.1.21Частично \ #OQ1.3-7
FR-5Вход выполнен успешноИнтерфейс переводит пользователя к следующему продуктовому сценарию после успешной проверки данных. По макету следующий шаг — 2.1 Поиск > Список сфер.Back: 1.1 Back: 1.1.21Частично \ #OQ1.3-4
FR-6Пользователь ввел неверные данныеИнтерфейс показывает единую ошибку входа без уточнения, какое поле неверно, и позволяет исправить идентификатор или пароль.Back: 1.1.22Частично \ #OQ1.3-2 \ #OQ1.3-8
FR-7Возникла ошибка соединенияИнтерфейс показывает ошибку соединения, сохраняет введенные данные и позволяет повторить отправку.2Частично \ #OQ1.3-3 \ #OQ1.3-9
FR-8Пользователь переходит к регистрации или к восстановлению пароляИнтерфейс переводит пользователя из входа в сценарий регистрации или восстановления пароля. Пользователь может перейти к этому шагу до начала авторизации или после ошибки.2Реализовано
FR-9Шаг входа успешно завершенИнтерфейс переводит пользователя к следующему продуктовому сценарию: Mobile 2.1 Поиск сфер.1Частично \ #OQ1.3-4

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

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
#OQ1.3-1Код FR-3Требование говорит: интерфейс применяет соответствующую маску/формат к идентификатору — телефону или email. В коде маска/формат есть только для телефона; для email ни маски, ни проверки формата — кнопка активируется по непустому полю, идентификатор уходит на бэкенд как есть. \ login_screen.dart:110; auth_text_field.dart:204-206Бизнес/РазработкаOpenКто валидирует формат email — фронт или бэкенд; согласовать с Back: 1.1.2. (А, рекоменд.) зафиксировать в FR-3, что маска только для телефона, а формат email проверяет бэкенд; (Б) добавить клиентскую проверку формата email до отправки.(Б) добавить клиентскую проверку формата email до отправки.
#OQ1.3-2Код FR-6Требование говорит: единая ошибка входа без указания, какое поле неверно. В коде UI показывает один блок ошибки без привязки к полю, но текст берётся из бэкенда (error.shortMessage) без пост-обработки — фиксированной клиентской строки нет, гарантия «не раскрывать поле» зависит от бэкенда. \ login_screen.dart:343; auth_repository.dart:111Бизнес/РазработкаOpen(А, рекоменд.) задать в BRD фиксированную клиентскую строку («Неверный логин или пароль») поверх бэкенд-сообщения, чтобы гарантировать «без указания поля»; (Б) сознательно доверять тексту бэкенда — зафиксировать это в FR-6. Текст ошибки согласовать с Back: 1.1.2.(Б) сознательно доверять тексту бэкенда — зафиксировать это в FR-6. Текст ошибки согласовать с Back: 1.1.2.
#OQ1.3-3Код FR-7Требование говорит: отдельный сценарий «ошибка соединения», в отличие от ошибки входа (FR-6). В коде клиент не различает сетевой сбой и неверные данные — обе ветки сводятся в один AuthStateError с текстом e.toString(); при обрыве связи пользователь увидит то же, что при неверном пароле. \ auth_bloc.dart:300-311Бизнес/РазработкаOpen(А, рекоменд.) развести состояния (сетевой сбой → отдельный тип ошибки со своим UI и «Повторить»); (Б) сознательно объединить и убрать FR-7 как отдельный сценарий, оговорив в BRD. Связать с OQ1.3-9 (повтор отправки).(А, рекоменд.) развести состояния (сетевой сбой → отдельный тип ошибки со своим UI и «Повторить»);
#OQ1.3-4Код FR-5, FR-9Требование говорит: после успеха — переход к «2.1 Поиск > Список сфер». В коде явного перехода на список сфер нет: после успеха управление возвращается на изначально запрошенный маршрут (resolver.next()), а «Список сфер» становится экраном лишь как первая вкладка стартового HomeRoute; плюс guard может увести на код-пароль/биометрию до сфер. \ app_router.dart:165,170-196; home_screen.dart:42Бизнес/РазработкаOpen(А, рекоменд.) переформулировать FR-5/FR-9 как «стартовая вкладка = Список сфер» и оговорить возможные вставки код-пароля/биометрии (шаг 1.3.1); (Б) закрепить явный переход на экран сфер. Вставки код-пароля/биометрии — тот же узел, что 1.3.1.(А, рекоменд.) переформулировать FR-5/FR-9 как «стартовая вкладка = Список сфер» и оговорить возможные вставки код-пароля/биометрии (шаг 1.3.1);
#OQ1.3-5Код вне BRDЧто нашли: код обрабатывает вход по временному паролю (IsTemporaryPassword) — пользователь помечается authenticated, но guard блокирует любые переходы до повторной авторизации. В BRD 1.3 этого сценария нет. \ auth_repository.dart:130; auth_bloc.dart:290-298; app_router.dart:139-154РазработкаOpenГайдбука у фронта нет, правило — на стороне продукта/Back. (А, рекоменд.) добавить отдельный UC/FR «вход по временному паролю»; (Б) явно вынести в scope-out «Не входит». Решение продукта; при необходимости сверить с Back: 1.1.2.От бизнеса: подняли дискуссию в чате задачи.
#OQ1.3-6Код FR-1Требование говорит: интерфейс показывает экран входа при запуске приложения (без активной сессии). В коде экран входа появляется не «при запуске», а когда guard перехватывает запрос защищённого маршрута и видит unauthenticated (начальный статус — по наличию валидного токена); перед авторизацией срабатывает онбординг — при непройденном онбординге сначала он. \ app_router.dart:114-166; initialization_processor.dart:317-320,128Бизнес/РазработкаOpenФункционально вход показывается при отсутствии сессии, но формулировка «при запуске» неточна. (А, рекоменд.) переформулировать FR-1 под guard + начальный статус по токену + приоритет онбординга; (Б) подтвердить, что «при запуске» трактуется именно так, и оставить формулировку.(А, рекоменд.) переформулировать FR-1 под guard + начальный статус по токену + приоритет онбординга; (это и есть при запуске)
#OQ1.3-7Код FR-4Требование говорит: отправка выбранного идентификатора вместе с паролем на проверку. В коде regionCode вычисляется, но его передача в вызов входа закомментирована → используется дефолт +7; поле кода региона readOnly, в UI есть FIXME про другие регионы. Вход фактически поддерживает только +7. \ login_screen.dart:383-389; auth_scope.dart:91; auth_repository.dart:71Бизнес/РазработкаOpen(А, рекоменд.) зафиксировать ограничение +7 в FR-4 (или scope-out мультирегиона); (Б) раскомментировать передачу regionCode и снять readOnly, если мультирегион нужен. Тот же корень, что в 1.2 (формат телефона, OQ1.2-2).(А, рекоменд.) зафиксировать ограничение +7 в FR-4 (или scope-out мультирегиона)
#OQ1.3-8Код FR-6Требование говорит: после ошибки — «позволяет исправить идентификатор или пароль». В коде красный текст ошибки рендерится, только пока state is AuthStateError; сброса ошибки при редактировании поля нет — она исчезает лишь при новой отправке (которая сначала эмитит AuthStateProcessing). \ login_screen.dart:340-347; auth_bloc.dart:267РазработкаOpenМелкий UX-выбор. (А) сбрасывать AuthStateError в onChanged полей («ошибка исчезает при правке»); (Б, рекоменд.) зафиксировать текущее — ошибка снимается при повторной отправке. Исправление данных не блокируется.(Б, рекоменд.) зафиксировать текущее — ошибка снимается при повторной отправке. Исправление данных не блокируется.
#OQ1.3-9Код FR-7Требование говорит: при ошибке соединения — «сохраняет введённые данные и позволяет повторить отправку». В коде контроллеры живут в State экрана и при ошибке не очищаются (данные сохраняются), но при пересоздании экрана сбросятся; отдельной кнопки «Повторить» нет — повтор = повторное нажатие «Продолжить». \ login_screen.dart:66-69; auth_bloc.dart:300-311Бизнес/РазработкаOpenЗавязано на разделение ошибок (OQ1.3-3). (А) развести сетевую ошибку в отдельное состояние и добавить кнопку «Повторить»; (Б, рекоменд.) задокументировать «повтор = повторное нажатие Продолжить» и сохранение данных в рамках сессии экрана. Связать с OQ1.3-3.(Б, рекоменд.) задокументировать «повтор = повторное нажатие Продолжить» и сохранение данных в рамках сессии экрана. Связать с OQ1.3-3.
#OQ1.3-10Код FR-3Проблема: заданный для поля телефона maxLength: 10 игнорируется (в TextField при phone выставляется maxLength: null); длину ограничивает сам форматтер (сброс ведущих 7/8, обрезка до 10 цифр), храня «сырые» цифры в отдельном контроллере. \ auth_text_field.dart:201,204-206; login_screen.dart:275РазработкаOpenДеталь реализации, продуктового вопроса нет. Закрываю сам: зафиксировать в BRD, что длину телефона ограничивает маска (10 цифр), а maxLength поля не используется → правка BRD. Правки кода не требуется.От бизнеса: внесли изменения в БРД.
#OQ1.3-11Код вне BRDЧто нашли: репозиторий печатает в лог логин и пароль в открытом виде при каждой попытке входа — риск утечки в логи/Sentry. \ auth_repository.dart:73-74РазработкаOpenЯвный риск безопасности, продуктового вопроса нет. Закрываю сам, приоритетно: убрать print логина/пароля (логировать только обезличенные данные) → тикет в Bitrix.От бизнеса: доработать, согласовать с Back. Доработка, вес 1.
#OQ1.3-12Код вне BRDЧто нашли: побочные эффекты входа, не отражённые в BRD — перед попыткой очищаются все токены; из ответа сохраняется InviteCode; AuthBloc подписан на очистку токенов и делает авто-logout при их сбросе (напр. по refresh-failure). \ auth_repository.dart:78,143-168; auth_bloc.dart:101-116Бизнес/РазработкаOpenРешение: отражать ли invite-code / авто-logout / предочистку токенов в требованиях 1.3 (как UC/техданные) или считать техническими деталями вне BRD. Продукт + разработка.считать техническими деталями вне BRD

Подстраницы


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

BRD 1.3 v0.1 · booking.mobile.flutter · commit dad0c65/dev · 2026-07-10 · якорь lib/src/feature/auth/widget/login_screen.dart (экран входа), auth/bloc/auth_bloc.dart, auth/data/auth_repository.dart, core/utils/widgets/auth_text_field.dart, core/utils/app_router.dart (guard), feature/initialization/logic/initialization_processor.dart (стартовый статус), feature/home/screen/home_screen.dart (вкладка «Список сфер»). Сводка: вопросов 12 (бизнесовых 5 / программных 7) · подтверждено без вопросов 2 (FR-2, FR-8) · отрезано находок обратного прохода 1 (аналитические события AppMetrica на каждое поле/клик — вне BRD, требований не затрагивают). Под особым вниманием: FR-3 (email без валидации формата), FR-5/FR-9 (приземление на дефолтную вкладку, а не гарантированный экран), FR-6/FR-7 (ошибка входа и ошибка соединения не разделены).

Бизнесовые

Связано сЧто в BRDЧто в кодеГде в коде
Б1FR-3Интерфейс применяет «соответствующую маску/формат поля» к идентификатору (телефон или email).Маска/формат есть только для телефона (_PhoneNumberFormatter); для email не применяется ни маска, ни проверка формата — единственное условие активации кнопки — непустое поле, идентификатор уходит на бэкенд как есть — нужно решение, кто валидирует формат email.login_screen.dart:110, auth_text_field.dart:204-206
Б2FR-6«Единая ошибка входа без уточнения, какое поле неверно».UI показывает один блок ошибки без привязки к полю, но текст берётся из бэкенда (error.shortMessage) и выводится без пост-обработки — фиксированной клиентской строки нет, гарантия «не раскрывать поле» зависит от бэкенда — нужно решение.login_screen.dart:343, auth_repository.dart:111
Б3FR-7Отдельный сценарий «ошибка соединения» — в отличие от ошибки входа (FR-6).Клиент не различает сетевой сбой и неверные данные: обе ветки сводятся в один AuthStateError с текстом e.toString(); при обрыве связи пользователь увидит то же сообщение, что и при неверном пароле — нужно решение, разделять ли эти состояния.auth_bloc.dart:300-311
Б4FR-5, FR-9После успеха — переход к «2.1 Поиск > Список сфер».Явного перехода на список сфер нет: после успеха управление возвращается на изначально запрошенный маршрут через resolver.next(); «Список сфер» становится экраном лишь как первая вкладка стартового HomeRoute. Плюс guard может увести на код-пароль/биометрию до сфер — нужно решение о формулировке «приземления».app_router.dart:165, home_screen.dart:42, app_router.dart:170-196
Б5вне BRDКод обрабатывает вход по временному паролю (IsTemporaryPassword): пользователь помечается authenticated, но guard блокирует любые переходы до повторной авторизации. В BRD 1.3 этого сценария нет — нужно решение: описать как UC или явно вынести в scope-out.auth_repository.dart:130, auth_bloc.dart:290-298, app_router.dart:139-154

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

  • Валидация идентификатора (Б1) — Вариант А (рекоменд.): зафиксировать в FR-3, что маска применяется только к телефону, а формат email проверяет бэкенд (клиент шлёт непустое значение). Вариант Б: добавить клиентскую проверку формата email до отправки (под-пункт FR-3/FR-4).
  • Текст ошибки входа (Б2) — Вариант А (рекоменд.): задать в BRD фиксированную клиентскую строку («Неверный логин или пароль») поверх бэкенд-сообщения, чтобы гарантировать «без указания поля». Вариант Б: сознательно доверять тексту бэкенда — тогда зафиксировать это в FR-6 как решение.
  • Ошибка входа vs соединения (Б3) — Вариант А (рекоменд.): развести состояния (сетевой сбой → отдельный тип ошибки со своим UI и «Повторить»). Вариант Б: сознательно объединить и убрать FR-7 как отдельный сценарий, оговорив это в BRD.
  • Приземление после входа (Б4) — Вариант А (рекоменд.): переформулировать FR-5/FR-9 как «стартовая вкладка = Список сфер» и оговорить возможные вставки код-пароля/биометрии. Вариант Б: закрепить явный переход на экран сфер (см. П-рекомендации).
  • Временный пароль (Б5) — Вариант А (рекоменд.): добавить отдельный UC/FR «вход по временному паролю». Вариант Б: явная строка в scope-out «Не входит».

Программные

Связано сЧто в BRDЧто в кодеГде в коде
П1FR-1«Интерфейс показывает экран входа при запуске приложения» (без активной сессии).Экран входа появляется не «при запуске», а когда guard перехватывает запрос защищённого маршрута и видит unauthenticated (начальный статус — по наличию валидного токена в хранилище). Перед авторизацией срабатывает проверка онбординга — при непройденном онбординге сначала показывается он.app_router.dart:114-166, initialization_processor.dart:317-320, :128
П2FR-4Отправка выбранного идентификатора вместе с паролем на проверку.regionCode вычисляется, но в вызов signInWithLoginAndPassword его передача закомментирована → используется дефолт +7; поле кода региона readOnly, в UI есть FIXME про «другие регионы». Вход фактически поддерживает только +7.login_screen.dart:383-389, auth_scope.dart:91, auth_repository.dart:71
П3FR-6«Позволяет исправить идентификатор или пароль» после ошибки.Красный текст ошибки рендерится, только пока state is AuthStateError; сброса ошибки при редактировании поля нет — она исчезает лишь при новой отправке (которая сначала эмитит AuthStateProcessing).login_screen.dart:340-347, auth_bloc.dart:267
П4FR-7«Сохраняет введённые данные и позволяет повторить отправку».Контроллеры живут в State экрана и при ошибке не очищаются (данные сохраняются), но при пересоздании экрана сбросятся. Отдельной кнопки «Повторить» нет — повтор = повторное нажатие «Продолжить».login_screen.dart:66-69, auth_bloc.dart:300-311
П5FR-3Маска/формат поля телефона.maxLength: 10, заданный для поля телефона, игнорируется (в TextField при phone выставляется maxLength: null); длину ограничивает сам форматтер (сброс ведущих 7/8, обрезка до 10 цифр), храня «сырые» цифры в отдельном контроллере.auth_text_field.dart:201, :204-206, login_screen.dart:275
П6вне BRDРепозиторий печатает в лог логин и пароль в открытом виде при каждой попытке входа — программный риск безопасности (утечка в логи/Sentry).auth_repository.dart:73-74
П7вне BRDПобочные эффекты входа, не отражённые в BRD: перед попыткой очищаются все токены; из ответа сохраняется InviteCode; AuthBloc подписан на очистку токенов и делает авто-logout при их сбросе (напр. по refresh-failure).auth_repository.dart:78, :143-168, auth_bloc.dart:101-116

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

  • FR-1 vs фактическая архитектура (П1) — Вариант А (рекоменд.): переформулировать FR-1 под guard + начальный статус по токену + приоритет онбординга («вход = первый защищённый переход после онбординга при отсутствии сессии»). Вариант Б: подтвердить, что «при запуске» трактуется именно так, и оставить формулировку.
  • Регион/+7 (П2) — Вариант А (рекоменд.): зафиксировать ограничение +7 в FR-4 (или scope-out мультирегиона). Вариант Б: раскомментировать передачу regionCode и снять readOnly, если мультирегион нужен.
  • Сброс ошибки (П3) — Вариант А: добавить сброс AuthStateError в onChanged полей («ошибка исчезает при правке»). Вариант Б (рекоменд., если поведение устраивает): зафиксировать текущее — ошибка снимается при повторной отправке.
  • Повтор отправки (П4) — Вариант А: развести сетевую ошибку в отдельное состояние и добавить кнопку «Повторить». Вариант Б (рекоменд.): задокументировать «повтор = повторное нажатие Продолжить» и сохранение данных в рамках сессии экрана.
  • maxLength телефона (П5) — деталь реализации: зафиксировать в BRD, что длину телефона ограничивает маска (10 цифр), а maxLength поля не используется. Правки кода не требуется.
  • Безопасность (П6) — приоритетно: убрать print логина/пароля из auth_repository.dart:73-74 (логировать только обезличенные данные).
  • Побочные эффекты (П7) — решить, отражать ли invite-code / авто-logout / предочистку токенов в требованиях 1.3 или считать их техническими деталями вне BRD.

1 item under this folder.