Направление работ: Mobile Код раздела: 1.3.1 Авторизация > Вход > Вход по код-паролю Статус: Готов Ответственный: Панкова Л.А. Версия: 0.1 Обновлено: 14.07.26
1. Назначение
Раздел описывает вход пользователя в мобильное приложение по ранее установленному код-паролю: напрямую без биометрической проверки или после неуспешной биометрической проверки.
Документ описывает фронтовые сценарии входа по код-паролю, состояние ошибки ввода и выход из аккаунта, если пользователь не может войти.
2. Ключевые понятия
Код-пароль - короткий цифровой пароль для повторного входа в мобильное приложение.
Биометрическая проверка - проверка пользователя через скан лица или отпечаток.
3. Scope
Входит
S1. Вход по код-паролю без биометрической проверки.
S2. Вход по код-паролю после неуспешной биометрической проверки.
S3. Состояние ошибки при неверном код-пароле.
S4. Выход из аккаунта, если пользователь не может войти.
Не входит
- Mobile 1.2 Регистрация
- Mobile 1.2.1 Задать код пароль и биометрию
- Mobile 1.3 Вход
- Mobile 1.4 Восстановление пароля
4. BR
Указаны в пункте 4 раздела Back: 1.1.2 Вход в систему.
5. Функциональные требования (UC + FR)
| ID | Сценарий | Функциональное требование | BR | Вес | Статус |
|---|---|---|---|---|---|
| S1: #FR1.3.1-1 | Пользователь открывает приложение после ранее выполненного входа в аккаунт и входит в приложение по код-паролю без биометрической проверки | Интерфейс открывает экран ввода ранее установленного код-пароля, показывает цифровую клавиатуру и прогресс ввода. | 1 | Реализовано | |
| S2: #FR1.3.1-2 | Пользователь открывает приложение после ранее выполненного входа в аккаунт, но скан лица или отпечаток не сработал | Интерфейс открывает экран ввода код-пароля, показывает цифровую клавиатуру, прогресс ввода и позволяет повторить биометрическую проверку тем способом, который не сработал: скан лица или отпечаток. | 1 | Частично \ #OQ1.3.1-3 \ #OQ1.3.1-4 | |
| S3: #FR1.3.1-3 | Пользователь вводит неверный код-пароль | Интерфейс показывает ошибку неверного код-пароля на экране ввода и позволяет повторить ввод без выхода из сценария. Лимитов на количество попыток ввода код-пароля нет. | 1 | Частично \ #OQ1.3.1-2 \ #OQ1.3.1-5 | |
| S4: #FR1.3.1-4 | Пользователь нажимает кнопку “Не могу войти” | Интерфейс показывает окно с текстом “Выйти из аккаунта и войти по номеру телефона или почте?” и кнопками “Выйти” и “Остаться”. По нажатию кнопки “Выйти” интерфейс завершает пользовательскую сессию и переводит пользователя в Mobile 1.3 Вход. По нажатию кнопки “Остаться” интерфейс закрывает окно и оставляет пользователя на экране ввода код-пароля. | 1 | Не реализовано \ #OQ1.3.1-1 |
6. Открытые вопросы
| ID | Источник | Вопрос | Кому | Статус | Рекомендации | Итоговое решение |
|---|---|---|---|---|---|---|
| #OQ1.3.1-1 | Код FR1.3.1-4 | Требование говорит: кнопка «Не могу войти» открывает окно «Выйти из аккаунта и войти по номеру телефона или почте?» с «Выйти»/«Остаться»; «Выйти» завершает сессию и ведёт в 1.3 Вход. В коде на экране пин-входа нет ни кнопки «Не могу войти», ни окна; экран без AppBar/«назад» — при забытом пине и без биометрии единственный выход это успешная биометрия. Логаут есть только в профиле. \ log_pin.dart:97-195; profile_title.dart:308-345 | Разработка | Open | Критично: без биометрии и при забытом пине пользователь заблокирован в приложении. FR1.3.1-4 описывает флоу полностью, продуктового выбора нет. Закрываю сам (приоритет): реализовать по BRD — кнопка «Не могу войти» + диалог, «Выйти» переиспользует AuthScope.signOut() и SecureCubit.onUnauthenticated() (сброс secure-состояния) → тикет в Bitrix. | реализовать по BRD — кнопка «Не могу войти» |
| #OQ1.3.1-2 | Код FR1.3.1-3 | Требование говорит: показать ошибку и «позволить повторить ввод без выхода из сценария». В коде повтор ничем не ограничен: нет счётчика попыток, блокировки или тайм-аута после серии неверных вводов (BRD про лимит молчит). \ log_pin.dart:144-188; secure_cubit.dart:136-147 | Бизнес/Разработка | Open | BRD про лимит молчит — не додумываю. Но 4-значный пин без лимита уязвим к перебору. (А, рекоменд.) ввести счётчик + нарастающий cooldown/блок после N неверных; (Б) осознанно оставить без лимита и зафиксировать в BRD. Продукт + безопасность. | (Б) осознанно оставить без лимита и зафиксировать в BRD |
| #OQ1.3.1-3 | Код FR1.3.1-2 | Требование говорит: повторить биометрию «тем способом, который не сработал: скан лица или отпечаток». В коде одна кнопка ретрая; метод выбирает ОС (biometricOnly), иконка — по платформе (iOS = Face ID, иначе отпечаток), а не по фактически доступному/не сработавшему методу. \ log_pin.dart:31-47,134-143,225-235 | Бизнес/Разработка | Open | (А, рекоменд.) показывать иконку/подпись по фактическому типу через существующий Utils.getTypeBiometric() (в пин-экране сейчас не вызывается); (Б) принять платформенную иконку как достаточную и уточнить FR1.3.1-2. Связать с OQ1.3.1-4 (тот же корень). | (Б) принять платформенную иконку как достаточную и уточнить FR1.3.1-2. Связать с OQ1.3.1-4 (тот же корень) |
| #OQ1.3.1-4 | Код FR1.3.1-2 | Проблема: иконка биометрии выбирается только по Platform.isIOS (iOS → face_id.svg, иначе → отпечаток), а не по реально доступному типу; Utils.getTypeBiometric() возвращает фактический тип, но в пин-экране не используется → на Android с распознаванием лица покажет отпечаток. \ log_pin.dart:225-235; utils.dart:963-1008 | Разработка | Open | Фикс — замена платформенной развилки на getTypeBiometric() с фолбэком на платформу. Зависит от решения по OQ1.3.1-3 (достаточно ли платформенной иконки). Связать с OQ1.3.1-3. | принять платформенную иконку как достаточную и уточнить FR1.3.1-2 |
| #OQ1.3.1-5 | Код FR1.3.1-3 | Проблема: ошибка неверного код-пароля выражена только тряской (_shake) и красным цветом точек; текстовое сообщение отключено (errorTextStyle fontSize:0), явной подписи «неверный код-пароль» нет. \ log_pin.dart:168-172,90-94,359-377 | Разработка | Open | FR1.3.1-3 требует показать ошибку; лейбл есть, но скрыт (fontSize:0) — похоже на случайное подавление (как в 1.2.1/OQ1.2.1-3). Закрываю сам: вернуть текст «Неверный код-пароль», тряску/красный оставить усилением → тикет в Bitrix. Если продукт считает цвет+тряску достаточными — отметить в FR1.3.1-3. | От бизнеса: доработка, вернуть текст ошибки, вес 1. |
| #OQ1.3.1-6 | Код вне BRD | Что нашли: при УСПЕШНОЙ биометрии вызывается Haptics.vibrate(HapticsType.error) — тактильный сигнал ошибки на успешную проверку (похоже на копипаст-баг). \ log_pin.dart:41-43 | Разработка | Open | Явный баг, продуктового вопроса нет. Закрываю сам: заменить HapticsType.error на HapticsType.success при успешной биометрии → тикет в Bitrix. | заменить HapticsType.error на HapticsType.success |
| #OQ1.3.1-7 | Код вне BRD | Что нашли: отладочные print в потоке пина выводят соль/хэш-параметры и «Pass = Pass» в логи; хэш пина хранится в SharedPreferences (ключ secure_hashPass), а не в Keychain/Keystore. \ secure_cubit.dart:110,119,127,140; secure_source.dart:23,40-48 | Разработка | Open | Закрываю сам (приоритет): убрать отладочные print с секретами перед релизом → тикет в Bitrix. Хранение хэша в SharedPreferences vs Keychain/Keystore — тот же вопрос, что в 1.2.1/OQ1.2.1-6; решение тех-лида по модели угроз ведём там, не дублирую. | убрать отладочные print с секретами перед релизом |
Вопросы по сверке с кодом
BRD 1.3.1 v0.1 · booking.mobile.flutter · commit dad0c65/dev · 2026-07-10 · якорь lib/src/feature/auth/log_pin/log_pin.dart + lib/src/feature/secure/ Сводка: вопросов 7 (бизнесовых 3 / программных 4) · подтверждено без вопросов 1 (FR1.3.1-1) · отрезано находок обратного прохода 1 (заглушка авто-локаута сессии: timeAutoLock=30, onAppActive/onAppInactive пустые — secure_cubit.dart:43-97) Под особым вниманием: FR1.3.1-4 (флоу «Не могу войти» отсутствует), FR1.3.1-2 (биометрия — метод/иконка), FR1.3.1-3 (видимость ошибки)
Бизнесовые
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| Б1 | FR1.3.1-4 | Кнопка «Не могу войти» → окно «Выйти из аккаунта и войти по номеру телефона или почте?» с «Выйти»/«Остаться»; «Выйти» завершает сессию и ведёт в 1.3 Вход | На экране пин-входа нет ни кнопки «Не могу войти», ни этого окна; экран без AppBar/кнопки «назад» — при забытом пине и без биометрии единственный выход это успешная биометрия. Логаут существует только в профиле («Выйти из аккаунта?»). — нужно решение: реализовать escape-флоу на экране пина | log_pin.dart:97-195 (нет кнопки/диалога); profile_title.dart:308-345 (логаут только в профиле) |
| Б2 | FR1.3.1-3 | Показать ошибку и «позволить повторить ввод без выхода из сценария» | Повтор ничем не ограничен: нет счётчика попыток, блокировки или тайм-аута после серии неверных вводов (BRD про лимит молчит). — нужно решение: вводить ли лимит попыток / cooldown для 4-значного пина | log_pin.dart:144-188 (checkPass без счётчика); secure_cubit.dart:136-147 |
| Б3 | FR1.3.1-2 | Повторить биометрию «тем способом, который не сработал: скан лица или отпечаток» | Одна кнопка ретрая биометрии; метод выбирает ОС (biometricOnly), иконка — по платформе (iOS = Face ID, иначе отпечаток), а не по фактически не сработавшему/доступному методу. — нужно решение: достаточно ли единой кнопки по платформе или различать face/fingerprint | log_pin.dart:31-47, 134-143, 225-235 |
Рекомендация / варианты решения
- Escape при невозможности входа (Б1) — критично: без биометрии и при забытом пине пользователь заблокирован в приложении. Вариант А (рекоменд.): реализовать по BRD — кнопка «Не могу войти» + диалог с «Выйти» (переиспользовать AuthScope.signOut(), ср. profile_title.dart:345, и SecureCubit.onUnauthenticated() secure_cubit.dart:79-84 для сброса secure-состояния) / «Остаться». Вариант Б: временно вести на восстановление (1.4) без полного логаута — но это расходится с текстом FR1.3.1-4.
- Лимит попыток (Б2) — Вариант А (рекоменд.): счётчик + нарастающий cooldown/блок после N неверных (защита от перебора 4-значного пина). Вариант Б: осознанно оставить без лимита и зафиксировать это в BRD.
- Различение метода биометрии (Б3) — Вариант А (рекоменд.): показывать иконку/подпись по фактическому типу через уже существующий Utils.getTypeBiometric() (utils.dart:963), который в пин-экране сейчас не вызывается. Вариант Б: принять платформенную иконку как достаточную и уточнить формулировку FR1.3.1-2.
Программные
| № | Связано с | Что в BRD | Что в коде | Где в коде |
|---|---|---|---|---|
| П1 | FR1.3.1-2 | Повтор биометрии тем способом, что не сработал (face / отпечаток) | Иконка биометрии выбирается только по Platform.isIOS (iOS → face_id.svg, иначе → Icons.fingerprint), а не по реально доступному типу; Utils.getTypeBiometric() возвращает фактический BiometricType, но в пин-экране не используется → на Android с распознаванием лица покажет отпечаток | log_pin.dart:225-235; utils.dart:963-1008 |
| П2 | FR1.3.1-3 | «Интерфейс показывает ошибку неверного код-пароля на экране ввода» | Ошибка выражена только тряской (_shake) и красным цветом точек; текстовое сообщение отключено (errorTextStyle fontSize:0). Явной подписи «неверный код-пароль» нет | log_pin.dart:168-172, 90-94, 359-377 |
| П3 | вне BRD | — | При УСПЕШНОЙ биометрии вызывается Haptics.vibrate(HapticsType.error) — тактильный сигнал ошибки на успешную проверку (похоже на копипаст-баг) | log_pin.dart:41-43 |
| П4 | вне BRD | — | Отладочные print в потоке пина выводят соль/хэш-параметры и «Pass = Pass» в логи; хэш пина хранится в SharedPreferences (ключ secure_hashPass), а не в Keychain/Keystore | secure_cubit.dart:110,119,127,140; secure_source.dart:23,40-48 |
Рекомендация / варианты решения
- Иконка/тип биометрии (П1, ср. Б3) — заменить платформенную развилку на Utils.getTypeBiometric() с фолбэком на платформу, если тип не определён; так иконка совпадёт с реально доступным методом.
- Видимая ошибка ввода (П2) — Вариант А (рекоменд.): вернуть текст ошибки (сейчас fontSize:0) с формулировкой «Неверный код-пароль»; тряску/красный оставить усилением. Вариант Б: если продукт считает цвет+тряску достаточными — зафиксировать это в BRD (иначе FR1.3.1-3 «показывает ошибку» выполнен лишь частично).
- Тактильный сигнал (П3) — заменить HapticsType.error на HapticsType.success при успешной биометрии.
- Логи и хранение секрета (П4) — убрать отладочные print с секретами перед релизом; рассмотреть перенос хэша в flutter_secure_storage (Keychain/Keystore). Вне BRD, но относится к безопасности пин-входа.