D4-4 과제 앱 TOTP MFA와 DB AAL2 강제

구현 요청
현재 앱에 Supabase TOTP MFA를 추가해줘. final 앱은 이메일/SNS 로그인 후 모두 AAL2를 확인해야 tasks를 열게 해줘. getAuthenticatorAssuranceLevel과 listFactors로 미등록·등록대기·기등록 AAL1·AAL2를 구분하고 enroll→QR/수동 키→challengeAndVerify→AAL2 확인을 연결해줘. 등록은 사용자 버튼으로 한 번만 시작하고 TOTP는 text/inputmode numeric/one-time-code/6자리 문자열로 받아 앞자리0을 보존해줘. SDK QR은 img로 표시하고 원본SVG를 innerHTML로 삽입하지 마. QR·비밀키·코드는 로그·URL·영구 저장·과제물에 남기지 않고 완료/취소/계정전환 시 메모리와 DOM에서 지워줘. 같은 세션의 중복 이벤트는 폼을 지우지 않게 하고 새 세션/계정 전환은 이전 요청을 무시해줘. 기존 user_id RLS/GRANT는 보존하고 tasks에 AS RESTRICTIVE FOR ALL TO authenticated, USING과 WITH CHECK로 aal2를 요구하는 SQL을 별도 파일로 만들어줘. 가상 계정의 TOTP 성공을 먼저 확인한 뒤 새 수업 DB에 적용하게 해줘. 틀린/지난 코드·재로그인 challenge·AAL1 차단/AAL2 본인 허용·타인 차단·미완료 등록 취소·분실 시 정식 복구 안내를 포함해줘. 운영 DB/기존 계정은 변경하지 마.

검증 요청
수업용 앱의 Google·Kakao·Naver 각각을 최초 가입/취소/재로그인/필요 동의 누락으로 검사해줘. Supabase user.id와 identities를 비교하고 같은 이메일만으로 기존 과제/역할을 옮기지 않는지 확인해줘. 네이버 어댑터는 고정 upstream·Bearer 검증·응답 오류·sub 매핑·이메일 합성 없음·no-store를 검사해줘. MFA는 등록 전→등록만 완료→잘못된 코드→검증 성공→로그아웃/재로그인→추가 인증을 확인해줘. DB Data API에서 본인 AAL1 조회0/쓰기거절, 본인 AAL2 CRUD 성공, B의 A 과제 접근 차단을 실제 세션으로 검사해줘. SQL Editor 관리자 결과나 버튼 숨김만으로 통과라 하지 마. QR·비밀키·인증코드·토큰은 보고서/스크린샷에 넣지 마. 실제 SNS 계정·배포·설정과 모의/로컬 검증을 따로 기록하고 공개 Vercel URL에서의 동작은 미실행이면 미확인으로 남겨줘.

실습 절차
1. 인증앱 등록→6자리 검증→AAL2: Supabase의 서비스 이용자 Auth MFA에서 TOTP 등록·검증을 활성화한다. 요청3으로 MFA 화면을 추가한다. 수업용 가상 계정으로 로그인해 등록 시작을 누르고 본인 인증앱에서 QR 또는 수동 키를 등록한다. 먼저 틀린 코드로 거절을 확인한 뒤 최신6자리 코드로 검증한다. 등록만 된 상태와 AAL2 세션을 구분한다.
2. DB AAL2 강제·재로그인·Vercel 최종 검증: 가상 계정의 AAL2 성공을 먼저 확인한 뒤 본인 수업 DB에서 sql/mfa-required.sql을 실행한다. 기존 user_id 소유자 정책을 유지한다. 로그아웃·재로그인하면 AAL1과 추가 인증 화면이 나오는지 검사한다. Data API에서 AAL1은 차단되고 AAL2 본인만 CRUD가 가능한지 A/B 계정으로 확인한다. Git PR 검토·병합과 Vercel 배포 후 같은 검사를 반복한다.

완료 확인
TOTP 입력은 문자열로 받아 앞자리0을 보존한다. 등록 성공 뒤 QR·키·입력 코드는 화면에서 제거한다. 코드를 자동 제출하지 않고 확인 버튼으로 검증한다. 이 설정은 Supabase 관리자 대시보드 MFA와 다르다.
AS RESTRICTIVE를 빠뜨리지 않는다. MFA 조건과 소유자 조건을 모두 만족해야 한다. 정책 적용 뒤 A/B 모두 MFA가 필요하다. 결과물에는 PR·배포commit·공개URL·제공자별 최초/취소/재로그인·MFA 전후 검사 결과를 기록하되 키·토큰·QR·코드는 넣지 않는다.