Skip to content

수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화 #597

Closed

Description

@OnlyReadOneBook

📜 Overview (Required)

수신자 본인 확인(이메일 인증) 결과가 발신자 구분 없이 전역 boolean 하나로 저장됩니다. 한 발신자에게 한 번 인증하면 그 기기에서 이후 모든 발신자에 대해 이메일 관문이 사라집니다.

IdentityVerificationRepositoryImpl

val VERIFIED = booleanPreferencesKey("identity_verified")   // senderId 없음
suspend fun markVerified()                                   // 파라미터 없음

DeliveryVerificationFlowViewModel 이 이 값을 보고 Intro 화면을 건너뛰어 마스터 키 단계로 바로 점프합니다(ReceiverNavGraph.kt:97).

재현

  1. 발신자 A 가 내 이메일로 나를 수신자 등록 → 앱에서 이메일 인증 통과 → identity_verified = true (DataStore 영구)
  2. 발신자 B 의 마스터 키를 입수 → 열람 신청 진입 시 이메일 인증 단계가 건너뛰어짐
  3. B 가 나를 수신자로 등록한 적이 없어도 마스터 키 검증 단계로 바로 들어감

이메일 인증의 역할이 "마스터 키만으로는 열 수 없다" 는 2차 확인인데, 전역 값이라 첫 1회로 무력화됩니다. markVerified() 호출 시점에 흐름 ViewModel 이 senderId 를 이미 보유하고 있어 키를 발신자별로 나누는 것으로 해결됩니다.

📌 Child Issue(Optional)

No response

📍 Note (Optional)

BE 쪽 선행 건이 있습니다 — Afternote-BE #74(/receiver-auth/email/verify 응답에서 accessCode 삭제). 07-29 release 실측 기준 아직 마스터 키를 그대로 내려주고 있어서, 그게 남아 있는 동안은 이메일 인증만 통과하면 마스터 키를 서버가 알려주는 구조입니다. 두 건이 모두 정리돼야 2단 확인이 성립합니다.

키를 identity_verified_<senderId> 로 바꾸면 기존 저장값은 무효가 됩니다. 이미 인증한 사용자가 한 번 더 인증하게 되는데, 지금 발신자 카드가 in-memory 라 앱 재시작 시 어차피 재등록이 필요해 실사용 영향은 없습니다.

Activity

  1. OnlyReadOneBook commented on Jul 29, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    경위 — stub 시절 구조가 남은 것

    의도한 설계가 아니라 이력의 산물입니다.

    캐시를 도입한 커밋 8a9ffa7a(2026-05-24) 시점에 이메일 인증은 IdentityEmailVerificationStub 이었습니다 — 클래스 KDoc 이 "백엔드 미구현 단계라 시뮬레이션한다", 고정 코드 000000, 네트워크 호출 0회. 서버 대조가 없으니 "이 단계를 통과했다" 는 플래그 하나로 충분했고, 발신자별이라는 개념이 필요 없었습니다. 그 커밋의 목적도 "앱 재시작 후에도 본인 확인 여부 유지"(영속화)뿐이었습니다.

    이후 #407 로 실 API(receiver-auth/email/*)가 붙으면서 이 단계의 의미가 "그 발신자가 등록해 둔 수신자 본인인가" 로 바뀌었는데, 캐시 구조는 따라가지 않았습니다.

    참고로 노션 IA 의 수신자 트리에는 "보안링크 접속 → 마스터키 입력" 만 있고 이메일 인증 단계 자체가 없습니다(https://app.notion.com/p/IA-2bd0982937878162a685c52e87d517cf). 이 단계는 IA 이후에 추가된 것이라 기획 문서로는 범위를 확인할 수 없고, 지금 구현된 실 API 의 계약(발신자가 등록한 수신자 이메일로 코드 발송, 미등록이면 404/1901)에 맞추면 발신자별이 맞습니다.

  2. changed the title [-]fix(afternote): 수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화[/-] [+]수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화[/+] on Aug 28, 2026
  3. OnlyReadOneBook commented on Aug 28, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    FE 몫(본인 확인 캐시의 발신자별 키 분리)은 PR #1371 로 반영됐다. 이 이슈는 열린 Afternote-BE#74(verify 응답의 accessCode 삭제)가 닫혀 2단 확인이 성립할 때 닫는다 — 머지 가드(blocked_by 검사)에 따라 PR 은 Refs 로만 연결.

  4. OnlyReadOneBook commented on Aug 29, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    해소 확인 — PR #1371 (머지됨)

    origin/develop 실측 결과 이 이슈가 지적한 구조가 남아 있지 않다. Closes 가 아니라 참조로 걸려 자동 종료되지 않은 것으로 보인다.

    이슈가 지적한 것

    val VERIFIED = booleanPreferencesKey("identity_verified")   // senderId 없음
    suspend fun markVerified()                                   // 파라미터 없음

    현행 (feature/receiver/data/.../IdentityVerificationRepositoryImpl.kt)

    override fun isVerified(senderId: String): Flow<Boolean> = ...
    override suspend fun markVerified(senderId: String) { verifiedSenderIds.update { it + senderId } }

    소비처도 발신자별로 갈렸다 — DeliveryVerificationFlowViewModel:52 가 isVerified(senderId) 를, IdentityVerificationViewModel:105 가 markVerified(senderId) 를 부른다. 전역 boolean 은 없다.

    커밋 2건이 순서대로 들어갔다

    커밋 내용
    316dd3656 본인 확인 캐시를 발신자별 키로 격리
    14a3e058a 그 캐시를 DataStore 에서 프로세스 수명 in-memory 로 되돌림

    두 번째 커밋은 재현 절차 1번의 전제(«identity_verified = true DataStore 영구»)까지 없앤다 — 키인 senderId 가 SenderRegistry 의 프로세스 수명 UUID 라 디스크에 남겨도 다시는 조회되지 않고 죽은 키만 쌓이기 때문이다. 구 릴리스가 남긴 전역 identity_verified 잔존값도 이 구현이 디스크를 읽지 않으므로 어떤 발신자의 관문도 열 수 없다.

    남는 갈래는 이 이슈 밖이다 — 프로세스를 넘는 발신자 식별자(BE 발급 sender id 또는 마스터 키의 비가역 파생값)가 생기면 영속을 되살릴지 다시 판단하게 되는데, 그 식별자는 #607(record-boxes 연동)이 만든다. 참고란의 BE #74 도 그쪽 전제다.

    해소되었으므로 닫는다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:afternote애프터노트 기능 영역bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions