Направление работ: Mobile Код раздела: 1.2.1 Авторизация > Регистрация > Задать код-пароль и биометрию Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 13.07.26


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

Раздел описывает опциональную настройку быстрого входа после завершения обязательной регистрации в мобильном приложении.

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

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

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

Код-пароль — короткий цифровой код из 4 цифр для быстрого входа в мобильное приложение на устройстве пользователя.

Биометрия — способ быстрого входа через системные механизмы устройства: Face ID или Touch ID.

Вход только по код-паролю — вариант, при котором пользователь не включает биометрию и использует только код-пароль.

Пропуск настройки — отказ от создания код-пароля в рамках регистрации с переходом к следующему шагу.

3. Scope

Входит

  • Предложение создать код-пароль после регистрации
  • Пропуск настройки код-пароля
  • Ввод и повтор код-пароля
  • Проверка совпадения код-пароля
  • Определение доступной биометрии на устройстве
  • Включение Face ID или Touch ID для входа
  • Отказ от биометрии с сохранением входа по код-паролю
  • Переход к следующему шагу регистрации
  • Не входит

  • Mobile: 1.2: Регистрация
  • Mobile: 1.3: Вход
  • Mobile: 1.2.2: Регистрация > Подтверждение почты
  • Mobile: 1.3.1: Вход по код-паролю
  • Mobile: 1.4: Восстановление пароля

4. BR

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

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

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

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

IDСценарийФункциональное требованиеBRВесСтатус
FR-1Пользователь переходит на шаг настройки быстрого входаИнтерфейс открывает шаг после завершения обязательной регистрации и предлагает создать код-пароль или пропустить настройку.1Частично \ #OQ1.2.1-4
FR-2Пользователь пропускает создание код-пароляИнтерфейс завершает шаг 1.2.1 без настройки быстрого входа и переводит пользователя к следующему шагу регистрации.1Реализовано
FR-3Пользователь выбирает создание код-пароляИнтерфейс открывает ввод цифрового код-пароля и показывает только действия, необходимые для ввода и удаления цифр.1Реализовано
FR-4Пользователь вводит код-парольИнтерфейс принимает только 4 цифры и переходит к повторному вводу после заполнения.1Реализовано \ #OQ1.2.1-11
FR-5Пользователь повторяет код-парольИнтерфейс сравнивает повторный ввод с первым значением код-пароля. Код-пароль совпал: Интерфейс сохраняет настройку код-пароля на устройстве и переходит к предложению биометрии, если устройство ее поддерживает. Код-пароль не совпал: Интерфейс показывает ошибку несовпадения и позволяет повторить ввод без возврата к началу регистрации.1Частично \ #OQ1.2.1-3 \ #OQ1.2.1-6
FR-6Проверка устройства на наличие функций биометрииИнтерфейс определяет, какая биометрия доступна на устройстве: Face ID, Touch ID или отсутствие биометрии.2Частично \ #OQ1.2.1-2 \ #OQ1.2.1-10
FR-7На устройстве доступен Face IDИнтерфейс предлагает включить Face ID или оставить вход только по код-паролю.2Реализовано
FR-8На устройстве доступен Touch IDИнтерфейс предлагает включить Touch ID или оставить вход только по код-паролю.2Реализовано
FR-9На устройстве нет биометрииИнтерфейс не предлагает Face ID или Touch ID и продолжает сценарий с входом по код-паролю.2Частично \ #OQ1.2.1-2
FR-10Пользователь выбирает вход только по код-паролюИнтерфейс сохраняет отказ от биометрии и переводит пользователя к следующему шагу регистрации.2Реализовано
FR-11Пользователь включает биометриюИнтерфейс запускает системную проверку Face ID или Touch ID и включает биометрический вход только после успешного подтверждения устройством.2Реализовано
FR-12Системная проверка биометрии не выполненаИнтерфейс оставляет доступным вход по код-паролю и позволяет повторить попытку или продолжить без биометрии.2Реализовано \ #OQ1.2.1-12
FR-13Шаг быстрого входа завершенИнтерфейс переводит пользователя к Mobile: 1.2.2 Подтверждение почты.1Реализовано

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

Пин контекста. BRD «1.2.1 Задать код-пароль и биометрию» v0.1. Репозиторий booking.mobile.flutter, ветка dev, commit 89ca50272c7e04b723ce665ce36f41df1158c7af. Дата сверки: 2026-07-15. Якорь домена: lib/src/feature/auth/widget/reg_ask_biometrics_screen.dart, lib/src/feature/auth/widget/pages/biometric/**, lib/src/feature/profile_page/profile_settings/screens/profile_PIN_reset_screen.dart, lib/src/feature/secure/**, lib/src/core/utils/utils.dart, lib/src/core/utils/app_router.dart. Стек: Dart / Flutter, BLoC/Cubit, плагин local_auth. Маршрутизация без сверки с гайдбуком (гайдбук не передавался). Исходный BRD (разделы 1–4) не изменялся.

Переобход относительно dad0c65 (2026-07-06): снято 4 вопроса — OQ1.2.1-1 (переход на 1.2.2 подтверждён после решения команды о переименовании шага), OQ1.2.1-5 и OQ1.2.1-9 (кнопка «Вход только по Код-паролю» ожила, мёртвая _checkAvailableBiometrics() удалена), OQ1.2.1-8 (пустые ячейки пинпада стали инертными). Осталось открытым 7 — OQ1.2.1-2, -3, -4, -6, -7, -10, -11 (код в этих местах не менялся). Появился 1 новый — OQ1.2.1-12 (PlatformException от local_auth не перехватывается на экране регистрации). Статусы ФТ: 6 требований перешли в «Реализовано» (FR-2, FR-3, FR-7, FR-8, FR-10, FR-13), «Не реализовано» не осталось. Механика экрана пин-кода переделана с роута на оверлей (AppLockGate + PinLockView, 7610f28/626cbde) — это относится к 1.3.1 (вне scope), но косвенно чинит 1.2.1: авто-лок больше не сбрасывает стек навигации на Home и не выбивает пользователя из цепочки регистрации.

Ограничение обхода. Проанализированы файлы флоу код-пароля и биометрии в регистрации, секьюр-слой и смежная навигация. НЕ проверялись: серверная часть, iOS/Android нативные разрешения (Info.plist / AndroidManifest — usage description для Face ID), сценарий входа (1.3.x), содержимое шага 1.2.2. Утверждения без file:line помечены как «не найдено за обход».

IDИсточникВопросКомуСтатусРекомендацииИтоговое решение
#OQ1.2.1-1Код FR-2, FR-13Требование говорит: после пропуска или завершения настройки быстрого входа пользователь переходит к следующему шагу регистрации — 1.2.2 Подтверждение почты. В коде перехода на подтверждение почты нет: «Пропустить» и завершение ведут на экран контактных данных (RegSetContactDataRoute), а он заканчивается переходом на главный экран (HomeRoute). Шага «1.2.2 Подтверждение почты» в цепочке навигации не найдено. \ reg_ask_pin_screen.dart:45; profile_PIN_reset_screen.dart:639; reg_set_contact_data.dart:257,282 Переобход 89ca502: исправлено — после решения команды о переименовании шага экран RegSetContactData и есть 1.2.2 (BRD 1.2.2 Scope: «Ввод имени и email», «Пропуск заполнения контактных данных»; в коде — поля имени и email, кнопка «Заполню потом» \ reg_set_contact_data.dart:43-44,265,300,303). Все три выхода из 1.2.1 ведут на 1.2.2: «Пропустить» \ reg_ask_pin_screen.dart:53; завершение без биометрии \ profile_PIN_reset_screen.dart:640-644; включение биометрии и отказ от неё \ reg_ask_biometrics_screen.dart:47-53,74-77. Цепочка 1.2.1 → 1.2.2 подтверждена.Бизнес (Герман)/РазработкаClosedПродуктовое решение о целевой цепочке. (А) если подтверждение почты (1.2.2) ещё не реализовано — оставить FR-2/FR-13 целевыми и завести задачу на переход к 1.2.2; (Б) если после регистрации целевой экран — главный, синхронизировать BRD с фактической цепочкой (контактные данные → главный). Тот же разрыв навигации, что и в 1.2 (OQ1.2-9 / FR-26) — решать вместе.У шага 1.2.2 изменено название с «Контактных данных» на «Подтверждение почты». Уточнить в коде, что после 1.2.1 идёт 1.2.2.
#OQ1.2.1-2Код FR-6, FR-9Требование говорит: интерфейс различает три исхода — Face ID, Touch ID или отсутствие биометрии. В коде определение типа возвращает больше вариантов: помимо face и fingerprint, на Android — iris, weak, strong; экран предложения показывает имя типа как есть, поэтому для них пользователь увидит «Вход по iris» / «weak» / «strong». BRD ограничивает биометрию Face ID и Touch ID. \ utils.dart:963-1009,997-1004; reg_ask_biometrics_screen.dart:53,55 Переобход 89ca502: частично — текст починен, суть вопроса осталась. Подпись теперь через switch: face → «Face ID», fingerprint → «Touch ID», остальное → «биометрии», сырых «iris»/«weak»/«strong» пользователь больше не увидит \ reg_ask_biometrics_screen.dart:28-32. Но getTypeBiometric по-прежнему возвращает iris/weak/strong \ utils.dart:1007-1012, поэтому на таком устройстве экран биометрии всё равно показывается («Вход по биометрии», иконка Touch ID \ reg_ask_biometrics_screen.dart:34-36), тогда как по BRD биометрии там нет и по FR-9 предлагать её не следует. Продуктовый вопрос не снят.Бизнес/РазработкаClosedПродуктовое решение о поддерживаемых типах. (А) расширить BRD (раздел 2) под Android-специфику (iris/weak/strong) и дать им человекочитаемые названия; (Б) трактовать iris/weak/strong как «нет биометрии по BRD» и ограничить определение типа face/fingerprint. Связать с OQ1.2.1-10 (поведение при нескольких типах).Доработать требования: привести FR-6/FR-9 к фактическому поведению — интерфейс не различает Face ID и Touch ID по отдельности.
#OQ1.2.1-3Код FR-5Требование говорит: при несовпадении повтора код-пароля интерфейс показывает ошибку несовпадения и позволяет повторить ввод. В коде при несовпадении поле очищается и проигрывается shake + вибрация, но текстовая ошибка не показывается — лейбл ошибки задан с fontSize: 0 (невидим), снэкбар/сообщение не выводятся. Возврата к началу регистрации нет (соответствует BRD). \ profile_PIN_reset_screen.dart:680-682,465-469 Переобход 89ca502: не исправлено — errorTextStyle c fontSize: 0 на месте \ profile_PIN_reset_screen.dart:465-469; ветка несовпадения по-прежнему только _shake() + pinController.clear() + вибрация, без текста \ profile_PIN_reset_screen.dart:683-687.РазработкаClosedFR-5 требует показать ошибку несовпадения; лейбл есть, но скрыт (fontSize: 0) — похоже на случайное подавление. Закрываю сам: вернуть видимый текст ошибки несовпадения → тикет в Bitrix. Если продукт решит, что shake достаточно — отметить это в FR-5 и убрать мёртвый лейбл.Признано дефектом. Доработать код: показывать текст ошибки несовпадения код-пароля (сейчас скрыт через fontSize: 0).
#OQ1.2.1-4Код FR-1Требование говорит: шаг настройки быстрого входа открывается после завершения обязательной регистрации. В коде вход на шаг реализован через guard роутера: при biometricConfirm == false любой незащищённый переход редиректится на шаг код-пароля, а сам флаг выставляется на экране пароля регистрации. То есть шаг привязан к флагу, а не строго к «завершению обязательной регистрации». \ app_router.dart:182-192; reg_password_screen.dart:96 Переобход 89ca502: не изменилось — guard редиректит на RegAskPinRoute при !biometricConfirm \ app_router.dart:105,176-190; флаг biometricConfirm: false выставляется там же, на экране пароля \ reg_password_screen.dart:94. Вопрос формальный и остаётся открытым.Бизнес/РазработкаClosedФактически шаг открывается после ввода пароля (последнего обязательного шага) — работает. Вопрос формальный: что считать «завершением обязательной регистрации» и не выставляется ли флаг преждевременно. (А) согласовать определение «завершения» и подтвердить корректность триггера; (Б) если триггер по флагу устраивает — зафиксировать это в FR-1.Вариант Б: скорректировать бизнес-требования под фактическую последовательность шагов регистрации.
#OQ1.2.1-5Код FR-11, FR-12Требование говорит: биометрия включается только после успешной системной проверки (FR-11); при неуспехе — оставить код-пароль и дать «повторить попытку или продолжить без биометрии» (FR-12). В коде FR-11 выполнен (биометрия активируется только при успешной local_auth). Но при неуспехе показывается снэкбар «Ошибка установки биометрии», пользователь остаётся на экране без явного «повторить / продолжить без биометрии»: повтор — только повторным нажатием той же кнопки, а «продолжить без биометрии» ведёт на мёртвую кнопку «Вход только по Код-паролю» (см. OQ1.2.1-9). \ utils.dart:1019-1037; reg_ask_biometrics_screen.dart:192-208,122-158 Переобход 89ca502: исправлено — «продолжить без биометрии» теперь работает: _skipBiometric вызывает _continueToContactData() (переход на 1.2.2, отказ сохраняется как TypeSecure.pass) \ reg_ask_biometrics_screen.dart:74-77,47-53. При didAuthenticate == false пользователь остаётся на экране со снэкбаром «Ошибка установки биометрии» и может либо повторить кнопкой, либо уйти по живой ссылке \ reg_ask_biometrics_screen.dart:55-72,60. FR-11 подтверждён повторно \ utils.dart:1028-1041. Не покрытый ветвью PlatformException случай вынесен в OQ1.2.1-12.Бизнес/РазработкаClosedFR-11 — реализовано. По FR-12 вопрос: обязателен ли явный элемент «продолжить без биометрии». (А, рекоменд.) признать обязательным и реализовать (совпадает с починкой кнопки в OQ1.2.1-9); (Б) если достаточно повтора нажатием — смягчить FR-12. Связать с OQ1.2.1-9.От разработки: реализовано.
#OQ1.2.1-6Код FR-5Проблема: код-пароль сохраняется не в защищённом хранилище (Keychain/Keystore), а в SharedPreferences: хранится Argon2-хэш (не открытый пин) под ключом secure_hashPass, флаг типа секьюрити — тоже в SharedPreferences. Контейнер — не secure storage. \ secure_cubit.dart:182-197; secure_source.dart:23,40-49,60-63 Переобход 89ca502: не исправлено — SecureSettingsSourceLocal по-прежнему наследует PreferencesDao (SharedPreferences), ключи secure_hashPass / secure_typeSecure \ secure_source.dart:18-27,40-49,60-63; хэш пишется через installPass \ secure_cubit.dart:217-231. Новое обстоятельство: flutter_secure_storage: ^9.0.0 уже в зависимостях \ pubspec.yaml:71, в проекте есть готовый SecureStorageDao \ secure_storage_dao.dart:9-13, и токены авторизации уже хранятся через него \ auth_storage_sp.dart:21. То есть пин-хэш — единственный секрет, оставшийся вне Keychain/Keystore; перенос стал дешевле.РазработкаClosedРешение тех-лида по модели угроз. (А) перенести хэш пина и флаг секьюрити в flutter_secure_storage (Keychain/Keystore); (Б) явно зафиксировать в BRD/тех-решении, что Argon2-хэш в SharedPreferences допустим. Хэш (не открытый пин) снижает риск, но контейнер незащищён.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.1-7Код вне BRDЧто нашли: в репозитории две независимые реализации ввода код-пароля — фактически используемая PINResetScreen (сравнение через статический класс PinCheck) и не подключённая install_pin/ (UserPinBloc), которая хранит пин в открытом виде в AppConfig.userPin. Второе — мёртвый код, расходящийся с фактическим хранилищем и потенциально небезопасный, если будет случайно подключён. \ profile_PIN_reset_screen.dart:719-735; install_pin_bloc.dart:61-183,96-100 Переобход 89ca502: не исправлено, объём мёртвого кода вырос. install_pin/ по-прежнему не подключён (InstallPinRoute упоминается только в роутере, push(InstallPinRoute()) в lib/ отсутствует \ app_router.dart:265,286), при этом его пинпад успели отрефакторить \ user_pin_screen.dart:223-224 — полируется мёртвый код. AppConfig.userPin (открытый пин) на месте \ app_config.dart:8; app_config_sp.dart:16,37. PinCheck со static late String pinFirst жив \ profile_PIN_reset_screen.dart:724-740. Добавилась третья реализация — PinLockView (экран-оверлей для входа, 1.3.1) \ pin_lock_view.dart:22, а LogPinScreen/LogPinRoute стали мёртвыми и помечены DEPRECATED «kept only to avoid a router regen» \ log_pin.dart:15-22.РазработкаClosedМёртвый и небезопасный код, продуктового выбора нет. Закрываю сам: удалить неиспользуемый install_pin/ (UserPinBloc/CheckPinCubit/UserPinStorageSP/InstallPinScreen) и AppConfig.userPin (хранение открытого пина) → тикет в Bitrix. Альтернатива, если install_pin/ — целевая архитектура: довести до рабочего состояния и переключить флоу на неё, убрав дубль PINResetScreen (тогда связать с OQ1.2.1-6).От бизнеса: почистить код. Доработка, вес 3.
#OQ1.2.1-8Код FR-3Проблема: пинпад показывает цифры и backspace, но также содержит пустые ячейки-заглушки, которые рендерятся как пустые неактивные кнопки. FR-3 требует «только действия, необходимые для ввода и удаления цифр» — функционально работает, но пустые кнопки лишние. \ profile_PIN_reset_screen.dart:167; user_pin_screen.dart:209-210 Переобход 89ca502: исправлено — пустая ячейка больше не рендерится кнопкой: pinpadLayout[index][0].isEmpty ? const SizedBox.shrink() : TextButton(...) на обеих страницах ввода \ profile_PIN_reset_screen.dart:252-255,585-588 (в коде помечено как «bug 002»). Пустых неактивных кнопок на пинпаде не осталось; FR-3 выполняется.РазработкаClosedМелкая уборка верстки, продуктового выбора нет. Закрываю сам: убрать пустые ячейки-заглушки из layout пинпада → тикет в Bitrix.От бизнеса: почистить код. Доработка, вес 3.
#OQ1.2.1-9Код FR-10Требование говорит: при выборе «вход только по код-паролю» интерфейс сохраняет отказ от биометрии и переводит к следующему шагу. В коде обработчик кнопки «Вход только по Код-паролю» на экране предложения биометрии не выполняет действий: навигация и сохранение отказа закомментированы, активна лишь отправка метрики — кнопка мертва. Плюс в файле есть неиспользуемая функция _checkAvailableBiometrics() с устаревшей логикой (на Android всегда возвращает TouchID). \ reg_ask_biometrics_screen.dart:122-158,121,249-267 Переобход 89ca502: исправлено — экран переписан целиком (335 строк изменений, 154 строки в итоге). Кнопка живая: _skipBiometric → метрика + _continueToContactData()push(RegSetContactDataRoute()) \ reg_ask_biometrics_screen.dart:74-77,47-53. Отказ сохраняется состоянием TypeSecure.pass, выставленным при installPass \ secure_cubit.dart:221-226 — «вход только по код-паролю» и есть это состояние. Мёртвая _checkAvailableBiometrics() удалена (grep по lib/ — совпадений нет). FR-10 выполняется.РазработкаClosedFR-10 задаёт поведение однозначно, продуктового выбора нет. Закрываю сам: реализовать обработчик кнопки (сохранить отказ + переход к следующему шагу) и удалить мёртвую _checkAvailableBiometrics() → тикет в Bitrix. Кнопка общая с FR-12 — починка закрывает и «продолжить без биометрии» (см. OQ1.2.1-5).Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.1-10Код FR-6Проблема: определение биометрии возвращает первый доступный тип по приоритету (face → fingerprint → iris → weak → strong). Если доступны и Face, и Touch, будет предложен только Face — выбора между ними нет. BRD рассматривает Face ID и Touch ID как взаимоисключающие исходы (FR-7/FR-8), что согласуется с «одним типом», но поведение при нескольких доступных типах явно не зафиксировано. \ utils.dart:979-1008 Переобход 89ca502: не изменилось — getTypeBiometric возвращает первый тип по тому же приоритету \ utils.dart:993-1016.РазработкаClosedЗамечание, не блокер. Закрываю сам: зафиксировать в BRD поведение «предлагается первый по приоритету при нескольких доступных типах» (FR-7/FR-8 остаются взаимоисключающими) → правка BRD. Связать с OQ1.2.1-2.Оставляем проверку кейсов на тестирование.
#OQ1.2.1-11Код FR-4, FR-5Что подтверждено: длина код-пароля «ровно 4 цифры» enforce-ится вводом (переход к повтору на 4-м символе), ввод ограничен цифрами клавиатурой пинпада — FR-4 выполнен. Замечание: проверка держится на порядке нажатий (length == 3), а не на валидаторе значения; отдельной валидации «только цифры»/длины на уровне модели нет. \ profile_PIN_reset_screen.dart:274-281,598-602,157-170 Переобход 89ca502: не изменилось — FR-4 по-прежнему выполняется, проверка по-прежнему на порядке нажатий: pinController.text.length == 3 на первом вводе \ profile_PIN_reset_screen.dart:273,283 и на повторе \ profile_PIN_reset_screen.dart:603,689. Та же схема воспроизведена в новом PinLockView \ pin_lock_view.dart:156,173 — хрупкость размножается.РазработкаClosedПродуктового вопроса нет. Закрываю сам: вынести длину/тип символов пина в явный валидатор/константу (устраняет хрупкость «по порядку нажатий») → тикет в Bitrix. \ Подтверждение: FR-4 работает, расхождения нет — правка только на живучесть кода.Не является дефектом — вопрос закрыт, доработок не требуется.
#OQ1.2.1-12Код FR-12Требование говорит: если системная проверка биометрии не выполнена, интерфейс оставляет доступным вход по код-паролю и позволяет повторить попытку или продолжить без биометрии. В коде покрыт только случай, когда пользователь отменил проверку (didAuthenticate == false) — тогда показывается снэкбар «Ошибка установки биометрии» \ reg_ask_biometrics_screen.dart:55-72,60. Но когда local_auth завершается ошибкой устройства (биометрия заблокирована после неудачных попыток, не зарегистрирована, недоступна), плагин бросает PlatformException; setBiometric её пробрасывает наружу \ utils.dart:1044-1046, а _enableBiometric не перехватывает \ reg_ask_biometrics_screen.dart:55-57. Исключение уходит в необработанные — пользователь не видит ни ошибки, ни подсказки: по нажатию кнопки «не происходит ничего». Вход по код-паролю и ссылка «Вход только по Код-паролю» при этом работают. Для сравнения: на экране входа тот же вызов обёрнут в try/catch \ pin_lock_view.dart:46-64,60-62 — поведение экранов расходится.РазработкаClosedПродуктового выбора нет: FR-12 требует обратной связи в ветке «проверка не выполнена», а самая частая её причина (lockout после нескольких неудачных попыток) сейчас молчит. Закрываю сам: обернуть _enableBiometric в try/catch по PlatformException и показать тот же снэкбар «Ошибка установки биометрии» (в идеале — с разбором кодов LockedOut / NotEnrolled / NotAvailable) → тикет в Bitrix. Проверить заодно, нужен ли rethrow в AppUtils.setBiometric \ utils.dart:1044-1046, раз ни один вызывающий его не ловит.Не является дефектом — вопрос закрыт, доработок не требуется.

0 items under this folder.