Эпик 12 — CoopID-восстановление (stacked на Эпик 11) #141

Closed
claude wants to merge 2 commits from feat/coopid-epic-12 into feat/coopid-epic-11
Owner

Stacked PR поверх feat/coopid-epic-11 (Эпик 11, PR #140). Восстановление доступа на CoopID (issue 105-15).

Story 12.1 (backend) — recovery-confirm пишет новый пароль в authentik

RecoveryFinalizationService раньше молча игнорировал новый пароль ("Эпик 5") → после recovery пайщик оставался залочен (vault под новым паролём, authentik помнил старый). Теперь финализация пишет пароль через admin set_password (порт 11.1).

Порядок setPassword → vault.store → changekey выбран по матрице частичных сбоев (внешний IdP — самый вероятный отказ, идёт первой → её сбой откатывает на старые креды; vault до changekey — ключ не теряется). Пароль не логируется/не хранится; регресс-тест на отсутствие в аудите.

Тесты 7/7, ESLint 0, тип-чек ts-jest.

Дальше в Эпике 12

  • 12.2 (SDK) loginWithMagicLink — полный confirm-флоу (сейчас notImplemented).
  • 12.3 (desktop) recover-флоу взамен LostKey/ResetKey — визуальная часть.
Stacked PR поверх feat/coopid-epic-11 (Эпик 11, PR #140). Восстановление доступа на CoopID (issue 105-15). ## Story 12.1 (backend) — recovery-confirm пишет новый пароль в authentik `RecoveryFinalizationService` раньше молча игнорировал новый пароль ("Эпик 5") → после recovery пайщик оставался залочен (vault под новым паролём, authentik помнил старый). Теперь финализация пишет пароль через admin set_password (порт 11.1). Порядок setPassword → vault.store → changekey выбран по матрице частичных сбоев (внешний IdP — самый вероятный отказ, идёт первой → её сбой откатывает на старые креды; vault до changekey — ключ не теряется). Пароль не логируется/не хранится; регресс-тест на отсутствие в аудите. Тесты 7/7, ESLint 0, тип-чек ts-jest. ## Дальше в Эпике 12 - 12.2 (SDK) loginWithMagicLink — полный confirm-флоу (сейчас notImplemented). - 12.3 (desktop) recover-флоу взамен LostKey/ResetKey — визуальная часть.
claude added 1 commit 2026-06-14 21:34:39 +00:00
Story 12.1 (Эпик 12, CoopID-восстановление). RecoveryFinalizationService раньше
молча игнорировал новый пароль (помечено «Эпик 5»): после recovery vault уже
зашифрован новым паролём, а authentik помнил старый → пайщик не мог войти ни
старым (vault не расшифровать), ни новым (authentik отвергает) паролём. Теперь
финализация пишет пароль через admin set_password (порт из 11.1, тот же, что
использует миграция 11.4).

Порядок записей setPassword → vault.store → changekey выбран по матрице частичных
сбоев трёх независимых хранилищ (authentik / vault-БД / on-chain): запись во
внешний IdP — самый вероятный отказ (недоступность, политика пароля), поэтому
идёт ПЕРВОЙ — её сбой не трогает vault и цепь, пайщик остаётся на старых кредах и
чисто повторяет восстановление. vault — ДО changekey (новый приватный ключ живёт
только в блобе, on-chain переключение коммитит его последним и ретраится).

findUserPk + guard: учётка authentik в recovery обязана существовать (recovery
требует включённого TOTP, а TOTP — authenticator authentik); null → защитный
throw (рассинхрон состояния), молча не создаём (нет email-контекста). Пароль
прозрачно уходит в authentik (единственный store паролей), не логируется/не
хранится на стороне controller'а; регресс-тест проверяет, что он не попадает в
аудит KeyRotated.

Тесты: 7/7 (вкл. порядок, null-guard, сбой setPassword=откат, без утечки пароля),
ESLint 0, тип-чек через ts-jest (полная типизация). DI байт-в-байт как MigrationService.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-14 22:03:46 +00:00
Был notImplemented-stub. Теперь полный confirm-флоу + повторный вход: генерация
новой пары (старый ключ при восстановлении утрачен), шифрование приватного новым
паролём в vault (AAD=субъект, наружу не уходит), POST /coop/recovery/confirm
{token, TOTP, public_key, vault, password} → сервер (12.1) ставит пароль в
authentik, сохраняет vault, ротирует active-ключ и отзывает сессии; затем authentik
новым паролём → unlockWallet (round-trip нового блоба) → timestamp-handshake.
Зачем: без этого фронт-recovery (12.3) нечем подтверждать — пайщик не мог
завершить восстановление и войти под новым ключом/паролём.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant added this to the CoopID project 2026-06-15 13:17:41 +00:00
Author
Owner

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.

Схлопнут в единый PR #148 (coopID → dev). Ветка сохранена как резерв, ничего не потеряно.
claude closed this pull request 2026-06-15 13:26:48 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#141