Skip to content

A1: 관리자 MFA (자체 TOTP) - #29

Merged
mack-erel merged 10 commits into
mainfrom
p4/a1-mfa
Oct 19, 2026
Merged

mack-erel merged 10 commits into
mainfrom
p4/a1-mfa

Conversation

@mack-erel

Copy link
Copy Markdown
Contributor

요약

4단계 A1 관리자 MFA(자체 TOTP)를 구현한다. Better Auth twoFactor 플러그인은 켜지 않고 앱 소유 모듈로 만든다.

  • A1.T1 저장소·TOTP·정책 모듈·마이그레이션 0007 (테이블 넷, 세션 칼럼 넷). 정책 수치는 MFA_* 배포 설정 필수값이고 기본값이 없다 (fail-closed)
  • A1.T2 /api/auth/admin-mfa/*, 부분 세션 제한(hooks.before), sessionAdmin MFA 판정
  • A1.T3 설치 직후 bootstrap grant·등록·복구 코드, 정책 미준비 설치 503(토큰 미소비)
  • A1.T4 step-up proof(작업 트랜잭션 안에서 소비)·승격·초기화·마지막 관리자·관리자 초대 수락 grant
  • A1.T5 봉인 단계 시험을 MFA fixture 로 이전, 봉인 검사 정의 불변 검사기 scripts/check-sealed-checks.mjs
  • A1.T6 Workers D1 전체 흐름
  • A1.T7 게이트 검사 교체와 CI 잡 p4-a1
  • 기존 관리자 전환 CLI apps/server/scripts/mfa-transition-grant.ts (grant 토큰은 표준 출력에 한 번)

시험

로컬 (guarded-run):

  • gate A1: 22개 중 20개 통과. G-A1.4 는 미커밋 생성물 diff 때문에 실패했고 커밋 뒤 깨끗함을 확인. G-A1.24(ruleset)는 건너뜀
  • 봉인 단계 재실행 K0K6·M0M5: 예상 밖 실패 0. 실패는 ruleset 필수 검사(A1 잡 미등록)와 미커밋 생성물 diff 뿐
  • M6·S0~S7 은 로컬에서 돌리지 않았다. 이 PR 의 CI 가 돈다

막힌 것

  • ruleset 필수 검사에 관리자 MFA (A1 게이트, 다섯 DB)가 없어 각 단계의 ruleset 검사(G-K0.31 등, G-A1.24)가 실패한다. ruleset 변경은 별도 승인 뒤 한다
  • 보안 리뷰와 gate A1 --seal 은 아직이다

🤖 Generated with Claude Code

mack-erel and others added 10 commits October 19, 2026 13:42
… 이메일 결합, D1 초대 수락 계약

- M1: 다른 관리자의 정지(admin.suspend)·삭제(admin.delete)는 대상 id 에 묶은 step-up proof 가 필요하다.
  대상이 관리자인지는 회원 관리 단위 안에서 잠근 행으로 판정하고, proof 소비는 그 단위의 첫 쓰기다. 회원 대상은 proof 를 쓰지 않는다.
- L2: 계정 단독 실패 칸(MFA_ACCOUNT_FAILURE_LIMIT, 1차 값 20)을 더한다. IP 를 바꾼 대입도 같은 지수 지연에 걸린다.
- L3: 관리자 초대 proof 대상을 고정값에서 정규화한 초대 이메일로 바꾼다.
- L4: D1 관리자 초대 수락 마무리의 guard 0행을 Node 와 같이 0 으로 돌려준다.
- G-A1.26(TC-A1.T4.f) 추가. L1 은 위험 수용, L5 는 A2 범위로 실행판에 남긴다.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…5.13·G-M5.15)

보안 리뷰 M1 고침으로 다른 관리자 정지에 step-up proof 가 필요해졌다. 시험 이름·선택식·기대 수는 바꾸지 않고 fixture 요청만 옮긴다.

Co-Authored-By: Claude Code <noreply@anthropic.com>
보안 리뷰 L2 로 더한 계정 단독 칸은 같은 계정의 동시 요청이 공유한다. 한 요청이 성공해 칸을 지우는 사이
다른 요청의 INSERT 가 충돌하고 UPDATE 가 0행이면 행이 없는데도 지연(429)으로 답했다.
행이 없으면 처음부터 다시 차지하고(최대 세 번), 그래도 판정하지 못하면 시도를 받지 않는다(fail-closed).
TC-A1.T2.f 실패 메시지에 상태·고정 코드를 남긴다.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@mack-erel
mack-erel merged commit 27dcaf4 into main Oct 19, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant