fix(member-social): 애플 로그인 aud 검증에 serviceId를 포함한다

This commit is contained in:
2026-03-30 09:21:59 +09:00
parent 2160e7b9dd
commit a4ffab0351
4 changed files with 109 additions and 3 deletions

View File

@@ -0,0 +1,50 @@
# 애플 로그인 aud 검증 실패 원인 분석
## 구현/분석 항목
- [x] `/member/login/apple` 요청 흐름과 `AppleIdentityTokenVerifier` 검증 로직을 확인한다.
QA: 관련 코드 경로와 실제 비교값(`audience` vs 설정값)을 파일 근거로 정리한다.
- [x] Apple Identity Token의 `aud` 규칙(웹 Service ID / 네이티브 Bundle ID)을 확인해 실패 원인을 확정한다.
QA: 공식 문서/신뢰 가능한 레퍼런스 근거를 함께 기록한다.
- [x] 필요 시 서버 검증 로직을 수정해 웹/앱 로그인 환경과 일치시키고, 불필요하면 수정하지 않는다.
QA: 수정 전/후 조건을 비교해 실패 지점 해소 여부를 설명한다.
- [x] 변경 사항에 대해 정적/실행 검증을 수행한다.
QA: 실행 명령과 성공/실패 결과를 기록한다.
## 검증 기록
- 1차 분석: 진행 전
- 무엇을: 애플 로그인 aud 검증 실패 재현 경로 분석을 시작했다.
- 왜: 62번째 줄 audience 검증 실패 원인을 코드/설정/외부 규격 기준으로 확정하기 위해서다.
- 어떻게: 코드 검색, 외부 문서 조사, 필요 시 테스트/빌드 검증을 수행할 계획이다.
- 2차 분석: 실패 원인 확정
- 무엇을: `/member/login/apple` 호출 경로와 Apple 토큰 audience 비교 대상을 확인했다.
- 왜: 실제 실패 지점이 검증 로직 문제인지, 설정 누락인지를 분리하기 위해서다.
- 어떻게: `MemberController.loginApple``AppleAuthService.authenticate``AppleIdentityTokenVerifier.validateClaims` 흐름을 확인했고,
`claims.audience.contains(bundleId)`(기존 62줄) 비교가 `apple.bundle-id` 단일값에만 의존함을 확인했다.
- 3차 분석: 외부 규격 대조
- 무엇을: Apple 공식 문서 기준으로 `id_token.aud` 의미를 확인했다.
- 왜: 웹 로그인에서 `aud` 기대값이 Bundle ID인지 Service ID인지 확정해야 수정 기준이 생긴다.
- 어떻게: Apple 문서에서 `aud == client_id`, 웹 Sign in with Apple JS는 `client_id`로 Service ID를 사용함을 확인했다.
따라서 웹 토큰의 `aud`가 Service ID일 때 기존 bundleId 단일 비교는 실패가 정상임을 확정했다.
- 4차 구현: 검증 로직 보완
- 무엇을: Apple 로그인 audience 검증 대상을 `bundleId` + `serviceId`로 확장했다.
- 왜: 웹(Service ID)과 앱(Bundle ID) 토큰 모두 동일 백엔드 검증 로직에서 처리하기 위해서다.
- 어떻게:
- `src/main/kotlin/kr/co/vividnext/sodalive/member/social/apple/AppleIdentityTokenVerifier.kt`
- `@Value("\${apple.service-id:}")` 추가
- `resolveExpectedAudiences()`로 유효 audience 집합 생성
- `isSupportedAudience()``claims.audience` 교집합 검증
- `src/main/resources/application.yml`
- `apple.serviceId: ${APPLE_SERVICE_ID:}` 추가
- `src/test/kotlin/kr/co/vividnext/sodalive/member/social/apple/AppleIdentityTokenVerifierTest.kt`
- bundleId/serviceId 허용 및 미일치 거부 케이스 추가
- 5차 검증: 정적/실행 확인
- 무엇을: 변경 코드의 테스트/린트/빌드를 수행했다.
- 왜: audience 로직 변경이 실제로 컴파일/테스트/스타일 검증을 통과하는지 확인하기 위해서다.
- 어떻게:
- `lsp_diagnostics` (Kotlin 파일): 로컬 환경에 `.kt` LSP 서버 미설정으로 도구 진단 불가(환경 제약 확인)
- `./gradlew test --tests "kr.co.vividnext.sodalive.member.social.apple.AppleIdentityTokenVerifierTest"` → 성공
- `./gradlew ktlintCheck build -x test` → 성공