77import io .jsonwebtoken .ExpiredJwtException ;
88import io .jsonwebtoken .Jwts ;
99import lombok .RequiredArgsConstructor ;
10+ import lombok .extern .slf4j .Slf4j ;
1011import org .springframework .http .ResponseEntity ;
1112import org .springframework .stereotype .Component ;
1213import org .springframework .web .client .RestTemplate ;
1314import org .springframework .web .util .UriComponentsBuilder ;
1415import ssu .eatssu .domain .auth .dto .AppleKeys ;
1516import ssu .eatssu .domain .auth .dto .OAuthInfo ;
17+ import ssu .eatssu .domain .user .repository .UserRepository ;
1618import ssu .eatssu .global .handler .response .BaseException ;
1719
1820import java .math .BigInteger ;
2931
3032@ Component
3133@ RequiredArgsConstructor
34+ @ Slf4j
3235public class SystemAppleAuthenticator implements AppleAuthenticator {
3336
3437 private final RestTemplate restTemplate ;
38+ private final UserRepository userRepository ;
3539
3640 public OAuthInfo getOAuthInfoByIdentityToken (String identityToken ) {
3741 PublicKey publicKey = generatePublicKey (identityToken );
@@ -42,30 +46,43 @@ public OAuthInfo getOAuthInfoByIdentityToken(String identityToken) {
4246 * 애플 로그인 - PublicKey 를 통해 유저 정보(providerId, email) 조회
4347 */
4448 private OAuthInfo getOAuthInfoByPublicKey (String identityToken , PublicKey publicKey ) {
45- // identityToken 에서 publicKey 서명을 통해 Claims 를 추출한다.
46- Claims claims = Jwts .parserBuilder ()
47- .setSigningKey (publicKey )
48- .build ()
49- .parseClaimsJws (identityToken )
50- .getBody ();
49+ Claims claims ;
50+ try {
51+ claims = Jwts .parserBuilder ()
52+ .setSigningKey (publicKey )
53+ .build ()
54+ .parseClaimsJws (identityToken )
55+ .getBody ();
56+ } catch (ExpiredJwtException exception ) {
57+ throw new BaseException (INVALID_IDENTITY_TOKEN );
58+ }
5159
5260 Object emailObj = claims .get ("email" );
5361 Object providerIdObj = claims .get ("sub" );
5462
5563 if (providerIdObj == null ) {
5664 throw new BaseException (NOT_FOUND_PROVIDER_ID );
5765 }
66+
67+ String providerId = providerIdObj .toString ();
68+
69+ // email 없는 경우 → Apple 재로그인 케이스 검증 (Apple 스펙상 최초 로그인 시에만 email 포함)
5870 if (emailObj == null ) {
59- throw new BaseException (NOT_FOUND_EMAIL );
71+ boolean existsUser = userRepository .findByProviderId (providerId ).isPresent ();
72+
73+ if (existsUser ) {
74+ // 가설 맞음: 기존 유저 재로그인 케이스
75+ log .info ("[Apple Login] email claim 없음 & DB 유저 있음. 재로그인 케이스로 확인. providerId={}" , providerId );
76+ throw new BaseException (NOT_FOUND_EMAIL );
77+ } else {
78+ // 다른 원인: 신규 유저인데 email 없음 → 슬랙 알럿용 별도 에러코드
79+ log .warn ("[Apple Login] email claim 없음 & DB 유저 없음. 원인 불명. providerId={}" , providerId );
80+ throw new BaseException (NOT_FOUND_APPLE_EMAIL_NEW_USER );
81+ }
6082 }
6183
62- try {
63- String email = emailObj .toString ();
64- String providerId = providerIdObj .toString ();
65- return new OAuthInfo (email , providerId );
66- } catch (ExpiredJwtException exception ) {
67- throw new BaseException (INVALID_IDENTITY_TOKEN );
68- }
84+ String email = emailObj .toString ();
85+ return new OAuthInfo (email , providerId );
6986 }
7087
7188 private PublicKey generatePublicKey (String identityToken ) {
0 commit comments