Skip to content

정리: SSO 연결을 끊어도 식별자를 묘비로 남겨, 같은 sub를 다른 주체에 다시 연결하지 못하게 한다 - #54

Merged
mack-erel merged 2 commits into
mainfrom
sso-tombstone
Oct 19, 2026
Merged

mack-erel merged 2 commits into
mainfrom
sso-tombstone

Conversation

@mack-erel

@mack-erel mack-erel commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

요약

SSO 연결을 끊어도 그 식별자를 묘비로 남겨서, 같은 sub를 다른 관리자나 다른 메일 계정에 다시 연결하지 못하게 합니다. 회원 공통 수치 결정 9("SSO 연결 식별자는 재사용 방지를 위해 남긴다")를 반영한 것이고, 계획의 P7입니다.

⚠ #51(P1) 위에 쌓은 PR입니다. base는 shared-throttle입니다. 마이그레이션 번호(028 다음 029)를 맞추려고 그렇게 했습니다. #51이 머지되면 base를 main으로 바꾸겠습니다.

왜

연결을 끊으면 행이 지워져서, 같은 sub를 다른 주체에 다시 연결할 수 있었습니다. IdP가 퇴사자의 sub를 신입에게 다시 주거나 운영자가 실수하면, 예전 사람의 IdP 로그인이 새 주체로 들어옵니다.

바뀐 것

  • 마이그레이션 029 identity_claims를 추가합니다. 새 테이블을 만들고, 이미 있는 연결을 그 주인으로 옮겨 적습니다(멱등).
  • 처음 연결할 때 주인을 기록하고 지우지 않습니다. 연결 행은 그 기록의 주인이 나일 때만 들어갑니다. 판정이 같은 배치 안에서 끝나므로, 해제와 다른 주체로의 연결이 동시에 와도 재사용되지 않습니다(리뷰 반영).
  • 다시 연결: 한 번 연결한 sub는 끊은 뒤에도 다른 주체에 연결하면 거절합니다. 원래 주인에게 다시 잇는 것은 허용합니다.
  • issuer 중복 금지: 같은 영역(account면 같은 테넌트)에서 같은 issuer를 두 id로 쓰면 기동을 거부합니다. 같은 IdP를 새 id로 다시 등록해 기록을 비켜 가지 못하게 하기 위해서입니다. 영역이 다르면(관리 콘솔용·셀프서비스용) 허용합니다.
  • LDAP/AD 디렉터리 연결은 대상에서 뺐습니다. SSO 식별자가 아니고, objectGUID는 재활용되지 않기 때문입니다.
  • 적용 범위: 관리 콘솔 관리자(admin_identities)와 메일 계정(directory_identities의 oidc: 행) 둘 다입니다.
  • 보존: 묘비에는 개인정보 없이 불투명 식별자만 남습니다. 그래서 탈퇴한 뒤에도 남겨 둡니다.

검증

  • npm run verify 통과. 테스트 2752개, 스모크 결과 마이그레이션 29개.
  • 로컬 임시 PostgreSQL 17과 MySQL 8에서 실DB 테스트를 돌렸습니다. CI에서도 PG·MySQL로 같은 테스트가 돕니다.
    • 동시에 들어온 첫 연결 6건 가운데 하나만 주인이 됩니다.
    • 해제와 다른 주체로의 연결을 동시에 10회 보내도 재사용이 0건입니다.
    • 029의 옮겨 적기는 두 번 돌려도 결과가 같습니다(멱등).
  • 독립 리뷰의 MEDIUM 3건을 반영했습니다. 그중 LDAP 건은 범위 밖으로 문서화했습니다.
  • 명령 테스트로 다음을 확인했습니다.
    • 끊은 sub를 다른 계정·관리자에 연결하면 거절됩니다.
    • 원래 주인에게는 다시 연결됩니다.
    • 다른 sub는 영향을 받지 않습니다.

배포 영향

  • 마이그레이션 029가 함께 나갑니다(새 테이블 + 기존 연결 옮겨 적기).
  • ⚠ 공급자 id를 바꾸거나 다른 IdP에 다시 쓰지 마세요. 연결 기록이 id로 묶입니다.

🤖 Generated with Claude Code

Base automatically changed from shared-throttle to main October 19, 2026 01:38
mack-erel and others added 2 commits October 19, 2026 10:57
왜(회원 공통 수치 2026-10-18 결정 9 "SSO 연결 식별자는 재사용 방지를 위해 남긴다"): 연결 해제가 행을 DELETE해서,
같은 sub를 다른 관리자·다른 메일 계정에 다시 연결할 수 있었다. IdP가 sub를 재활용하거나(퇴사자 sub를 신입에게)
운영자가 실수하면 예전 사람의 IdP 로그인이 새 주체로 들어온다.

- 마이그레이션 029 `identity_tombstones`(realm·provider·subject·owner_id·retired_at). 해제는 행 삭제와 묘비를 한
  배치로 한다(insertIgnore — 같은 sub를 여러 번 끊어도 첫 기록을 남긴다).
- 연결할 때 그 sub의 묘비가 **다른 주인**이면 "retired"로 거절한다(명령은 conflict + 사유). 원래 주인에게 다시 잇는
  것은 허용한다(실수로 끊은 것을 되돌릴 수 있게).
- 관리자(admin_identities)와 메일 계정(directory_identities의 oidc: 행) 둘 다. 개인정보가 아니라 불투명 식별자뿐이라
  탈퇴 뒤에도 남긴다. 결정의 "iss"는 공급자 설정 id로 대신한다(설정 하나가 issuer 하나를 고정한다).
- 문서: SCHEMA §4-5·마이그레이션 표, OPERATIONS E3, PROTOCOLS 표.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…뷰 반영)

왜(리뷰 MEDIUM):
- 묘비를 읽은 뒤 연결을 넣는 사이에 해제가 끼면(PG READ COMMITTED) 묘비가 있는데도 sub가 다른 주체에 연결됐다.
  이제 sub를 처음 연결할 때 주인을 기록하고(`identity_claims`, INSERT IGNORE) 지우지 않는다. 연결 행은 그 기록의
  주인이 나일 때만 들어간다(INSERT … SELECT … WHERE owner_id = ?, 한 배치). 해제는 행만 지운다. 029가 아직 어디에도
  적용되지 않아 테이블을 이 설계로 바꿨고, 이미 있는 연결은 그 주인으로 옮겨 적는다(NOT EXISTS로 멱등).
- 기록 키가 issuer가 아니라 공급자 id라 같은 IdP를 새 id로 다시 등록하면 기록을 비켜 갔다. 같은 영역(account면 같은
  테넌트)에서 같은 issuer를 두 id로 쓰면 기동을 거부한다. 영역이 다르면(관리 콘솔·셀프서비스 클라이언트) 허용한다.
- 안 들어간 연결은 지금 다른 주체에 연결돼 있으면 conflict, 예전에 연결됐던 sub면 retired로 구분한다(안내 문구).
- 문서: LDAP 디렉터리 연결은 대상 밖(SSO 식별자가 아니고 objectGUID는 재활용되지 않는다), 공급자 id를 바꾸거나
  다시 쓰지 말 것, account 영역 기록은 테넌트와 무관하게 전역.
- 실DB 테스트(PG 17·MySQL 8, 로컬 임시 컨테이너): 동시 첫 연결 6건 중 하나만 주인, 해제와 다른 주체 연결 동시 10회
  재사용 0, 029 옮겨 적기 멱등. 다른 테넌트의 같은 sub 테스트는 행을 직접 넣어 같은 상황을 만든다(이제 명령으로는 못 만든다).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mack-erel
mack-erel merged commit 8642571 into main Oct 19, 2026
9 checks passed
@mack-erel
mack-erel deleted the sso-tombstone branch October 19, 2026 02:03
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