결정-0022: 소셜 제공자 토큰을 보관하지 않고 스키마에서 칸을 없앤다
상태
승인됨
맥락
당시 전제는 소셜 제공자 2종(구글 · 카카오)이며 인증 스키마 마이그레이션이 아직 어디에도 배포된 적이 없다는 것입니다.
accounts 테이블은 처음부터 액세스 토큰과 리프레시 토큰, 만료 시각 세 칸을 갖고 있었습니다. 제공자가 발급한 토큰을 담기 위한 자리이며 자체 세션 토큰이 아닙니다. 액세스 토큰은 수명이 짧고 리프레시 토큰은 사용자가 화면 앞에 없을 때 새 액세스 토큰을 받기 위한 것입니다. 즉 이 세 칸의 존재 이유는 하나뿐입니다. 사용자를 대신해 그 제공자의 API를 호출하는 것입니다.
AUTH-02와 AUTH-03 구현 시점에 토큰을 저장하지 않기로 이미 판단했습니다. 쓸 데가 없고 쓰지 않을 자격증명은 유출 시 피해만 키우기 때문입니다. 다만 칸은 스키마에 남겼습니다. 채우면 되니 자리는 두자는 판단이었습니다.
그 결과 코드가 이렇게 되었습니다. 소셜 로그인 성공 처리기가 세 자리에 null 을 넣고, 그 null 이 신원 객체와 애그리거트, 저장소 어댑터를 지나 DB의 빈 칸에 도달합니다. 다섯 곳을 통과하는 값이 항상 null 이었습니다. 검토자가 스키마를 읽다가 이 값이 왜 필요한지를 물었고 그것이 본 결정의 계기입니다.
검토한 대안
칸을 남기고 주석으로 의도를 박습니다
| 구분 | 내용 |
|---|---|
| 장점 | 나중에 필요해지면 마이그레이션 없이 채울 수 있습니다 |
| 단점 | 죽은 칸이 계속 남고 null 만 흐르는 파라미터 세 개가 도메인과 저장소, 핸들러의 시그니처를 계속 부풀립니다 |
| 기각 사유 | 칸이 있으니 채우자는 유혹이 남습니다. 그때 평문 저장이 되면 자기 DB 유출이 남의 서비스 침해로 번집니다 |
칸을 남기고 지금 암호화 저장까지 구현합니다
쓰지 않는 값을 위해 키 관리와 회전 설계를 먼저 하는 셈이므로 순서가 거꾸로입니다. 기각합니다.
칸을 지웁니다 (채택)
스키마와 도메인, 저장소에서 함께 없앱니다.
결정
accounts 에서 액세스 토큰과 리프레시 토큰, 만료 시각 세 칸을 제거합니다. 신원 객체와 애그리거트에서도 대응 필드를 없앱니다.
해당 마이그레이션 파일을 직접 수정하며 새 마이그레이션을 추가하지 않습니다. 이 마이그레이션은 아직 어디에도 배포되지 않았습니다. 배포 이력이 없는 마이그레이션에 drop column 마이그레이션을 얹으면 이 저장소를 받는 쪽이 만들었다 지우는 이력을 그대로 물려받습니다.
본 결정은 결정-0015의 연장선입니다. 제공자에게서 받은 것을 필요 이상으로 들고 있지 않는다는 같은 원칙입니다. 이메일을 신뢰하지 않고 타입에서 없앴듯이 토큰도 담을 자리 자체를 없앱니다. 없는 칸은 잘못 채워질 수 없습니다.
결정-0014 결과 절의 마지막 항목을 본 결정이 대체합니다. 결정-0014의 본 결정인 불투명 세션 토큰과 DB 조회는 유효하며 영향받지 않습니다. 그 필드는 애초에 자체 세션과 무관한 제3자 토큰이었으므로 그것을 없애는 것이 세션 방식을 건드리지 않습니다.
결과
- 로그인 경로에서
null세 개가 사라집니다. 신원 객체는 제공자와 신원 식별자 둘만 남아 제공자가 증명하는 것이 신원 하나뿐이라는 사실을 타입이 그대로 말합니다. - 토큰을 보관하지 않는다를 테스트로 지킬 필요가 없어집니다. 담을 자리가 없으므로 컴파일러가 지킵니다. 기존의 널 단언은 함께 제거했습니다.
- 감수하는 것은 제공자 API 위임 호출이 필요해지면 마이그레이션이 하나 더 든다는 점입니다. 그때는 칸을 되살리는 것으로 끝나지 않습니다. 저장 시 암호화와 키 회전을 함께 결정해야 하고 그것은 새 결정기록의 몫입니다. 칸이 남아 있었다면 그 결정 없이 채워질 수 있었다는 점에서 이 비용은 대가가 아니라 안전장치입니다.
- 로컬 개발 DB는 마이그레이션 체크섬이 어긋나므로 스키마를 지우고 다시 만들어야 합니다. 배포된 환경이 없으므로 운영 영향은 없습니다.
재검토 조건
- 제공자 API를 사용자 대신 호출하는 요구사항이 생길 때 본 결정을 대체하는 새 결정기록을 쓰되 저장 시 암호화와 키 회전, 토큰 폐기 시점을 함께 정합니다. PROF-04(소셜 연동 관리)가 그 후보입니다.
- 제공자 API 연동을 핵심 기능으로 삼는 프로젝트가 이 아키텍처를 채용하는 경우에는 여기에 미리 넣지 않고 그 프로젝트에서 위 조건에 따라 추가합니다.