Skip to content

Feature/#28 gogole apple login#32

Merged
yyytir777 merged 9 commits intodevelopfrom
feature/#28-gogole-apple-login
Dec 16, 2025
Merged

Feature/#28 gogole apple login#32
yyytir777 merged 9 commits intodevelopfrom
feature/#28-gogole-apple-login

Conversation

@yyytir777
Copy link
Contributor

@yyytir777 yyytir777 commented Dec 15, 2025

📝 상세 내용

  • 애플 디벨로퍼스 구매 완
  • Apple ID 발급
  • 애플 로그인 구현
  • RN에서 idToken발급 후 백엔드로 전달 -> 해당 토큰에 대한 이메일 정보를 추출 (JWT) -> 추출한 이메일 정보로 유저 로그인 진행
  • POST /api/v1/auth/apple/login
  • POST /api/v1/auth/apple/singup

🔗 관련 이슈

Summary by CodeRabbit

  • 새로운 기능

    • Apple 로그인 시 JWT 토큰 검증 기능이 활성화되었습니다.
    • Apple 이메일 검증 오류 코드가 추가되었습니다.
  • 설정

    • Apple 및 Naver 클라이언트 인증 설정이 업데이트되었습니다.
    • 플랫폼 필드의 유효성 검사 규칙이 변경되었습니다.
    • 데이터베이스 마이그레이션 전략이 수정되었습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 15, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

이 PR은 Google 및 Apple 로그인 기능을 구현합니다. GoogleAndAppleSignupRequest의 platform 필드 검증 제약을 @NotBlank에서 @NotNull로 변경하고, AuthService에 JWT 기반 Apple ID 토큰 디코딩 및 검증 로직을 추가합니다. 검증 절차는 발급자(issuer)가 https://appleid.apple.com인지, 대상(audience)이 설정된 appleClientId와 일치하는지를 확인하고, 토큰 클레임에서 이메일을 추출합니다. 추가로 NOT_EXISTS_EMAIL 에러 코드가 신규 등록되며, 설정 파일에 Apple, Naver 설정 블록이 추가되고 Naver 관련 설정이 일부 제거됩니다.

Sequence Diagram

sequenceDiagram
    participant Client
    participant AuthService
    participant JwtDecoder
    participant TokenClaims as Token Claims
    
    Client->>AuthService: getEmailFromIdToken(idToken)
    AuthService->>JwtDecoder: decode(idToken)
    JwtDecoder-->>AuthService: Jwt object
    
    alt issuer validation
        AuthService->>TokenClaims: validate issuer == appleid.apple.com
        TokenClaims-->>AuthService: ✓ valid
    else issuer invalid
        AuthService-->>Client: JwtException → INVALID_TOKEN
    end
    
    alt audience validation
        AuthService->>TokenClaims: validate audience == appleClientId
        TokenClaims-->>AuthService: ✓ valid
    else audience invalid
        AuthService-->>Client: JwtException → INVALID_TOKEN
    end
    
    alt email extraction
        AuthService->>TokenClaims: get email from claims
        TokenClaims-->>AuthService: email value
        AuthService-->>Client: return email
    else email missing
        AuthService-->>Client: NOT_EXISTS_EMAIL
    end
Loading

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목에 오타가 있고 'gogole'은 'google'의 오타이며, 전체적으로 모호한 표현입니다. 제목을 'Implement Google and Apple login' 또는 'Feature/28: Google and Apple login'으로 수정하여 명확하고 오류 없는 제목으로 변경하시기 바랍니다.
Out of Scope Changes check ⚠️ Warning application-local.yaml에서 Google 설정의 잠재적 중복 키 문제와 Naver 설정 제거는 범위를 벗어난 변경으로 보입니다. application-local.yaml의 google 설정 중복 키 문제를 해결하고, 범위 내 변경만 유지하여 설정 파일의 일관성을 확보하시기 바랍니다.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed PR의 코드 변경사항이 Google과 Apple 로그인 구현의 주요 요구사항을 충족합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/resources/application-local.yaml (1)

32-38: 중복된 google 키가 존재합니다.

YAML 파일에 google 블록이 두 번 정의되어 있습니다 (Lines 32-34, 36-38). YAML 파서에 따라 마지막 값으로 덮어씌워지거나 파싱 오류가 발생할 수 있습니다.

중복 블록을 제거하세요:

 google:
   android-id: ${GOOGLE_ANDROID_CLIENT_ID}
   ios-id: ${GOOGLE_IOS_CLIENT_ID}
-
-google:
-  android-id: ${GOOGLE_ANDROID_CLIENT_ID}
-  ios-id: ${GOOGLE_IOS_CLIENT_ID}
src/main/java/ita/tinybite/domain/auth/service/AuthService.java (1)

259-261: 지원되지 않는 LoginType에 대해 예외를 던져야 합니다.

현재 GOOGLEAPPLE 외의 LoginType이 전달되면 null이 반환됩니다. 이는 호출자에서 NullPointerException을 유발할 수 있습니다.

         }
+        default -> throw BusinessException.of(AuthErrorCode.INVALID_PLATFORM);
     }
-    return null;
 }

또는 switch expression 형태로 변경:

return switch(loginType) {
    case GOOGLE -> { /* Google logic */ }
    case APPLE -> { /* Apple logic */ }
    default -> throw BusinessException.of(AuthErrorCode.INVALID_PLATFORM);
};
🧹 Nitpick comments (1)
src/main/java/ita/tinybite/domain/auth/service/AuthService.java (1)

229-231: 예외 로깅 추가 권장

일반 Exception을 잡을 때 디버깅을 위해 예외 내용을 로깅하는 것이 좋습니다.

 } catch (Exception e) {
+    log.warn("Google ID token verification failed: {}", e.getMessage());
     throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d03fd30 and 2b0e0e5.

📒 Files selected for processing (6)
  • src/main/java/ita/tinybite/domain/auth/dto/request/GoogleAndAppleSignupRequest.java (2 hunks)
  • src/main/java/ita/tinybite/domain/auth/service/AuthService.java (3 hunks)
  • src/main/java/ita/tinybite/global/exception/errorcode/AuthErrorCode.java (1 hunks)
  • src/main/resources/application-dev.yaml (1 hunks)
  • src/main/resources/application-local.yaml (1 hunks)
  • src/main/resources/application.yaml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/resources/application.yaml (1)
src/main/java/ita/tinybite/global/config/AppleConfig.java (2)
  • Configuration (8-19)
  • Bean (13-18)
🔇 Additional comments (6)
src/main/resources/application-dev.yaml (1)

15-15: LGTM!

개발 환경에서 update 설정은 적절합니다. 주석으로 환경별 권장 설정을 명시한 것도 좋습니다.

src/main/resources/application.yaml (1)

24-29: LGTM!

Apple 및 Naver 설정 블록이 올바르게 추가되었습니다. AppleConfig.java에서 apple.client-id를 사용하는 것과 일치합니다.

src/main/resources/application-local.yaml (1)

40-42: 주석 처리된 Apple 설정 확인 필요

application.yaml에 정의된 apple.client-id가 local 환경에서도 적용됩니다. 로컬 테스트용 별도 Apple Client ID가 필요한 경우 주석을 해제하고 환경 변수를 설정해야 합니다.

src/main/java/ita/tinybite/domain/auth/dto/request/GoogleAndAppleSignupRequest.java (1)

16-17: LGTM!

PlatformType은 enum 타입이므로 @NotBlank 대신 @NotNull을 사용하는 것이 올바릅니다. @NotBlank는 String 타입에만 적용됩니다.

src/main/java/ita/tinybite/global/exception/errorcode/AuthErrorCode.java (1)

19-21: LGTM!

INVALID_PLATFORMNOT_EXISTS_EMAIL 에러 코드가 적절하게 추가되었습니다. Apple 로그인 시 이메일이 존재하지 않는 경우를 명확히 처리할 수 있습니다.

src/main/java/ita/tinybite/domain/auth/service/AuthService.java (1)

52-53: LGTM!

appleClientId 필드가 @Value를 통해 올바르게 주입됩니다.

Comment on lines +243 to +246
String aud = jwt.getAudience().get(0);
if (!aud.equals(clientId)) {
throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Audience 리스트 비어있는 경우 방어 코드 추가

jwt.getAudience()가 빈 리스트를 반환하면 IndexOutOfBoundsException이 발생할 수 있습니다.

-String aud = jwt.getAudience().get(0);
-if (!aud.equals(clientId)) {
+if (jwt.getAudience().isEmpty() || !jwt.getAudience().get(0).equals(clientId)) {
     throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String aud = jwt.getAudience().get(0);
if (!aud.equals(clientId)) {
throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
}
if (jwt.getAudience().isEmpty() || !jwt.getAudience().get(0).equals(clientId)) {
throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
}
🤖 Prompt for AI Agents
In src/main/java/ita/tinybite/domain/auth/service/AuthService.java around lines
243-246, the code assumes jwt.getAudience() has at least one element and
directly accesses index 0 which can throw IndexOutOfBoundsException; add a
defensive check that jwt.getAudience() is not null and not empty before reading
index 0, and if it is null/empty throw
BusinessException.of(AuthErrorCode.INVALID_TOKEN) (or use the same error path as
other invalid audience cases) so the failure is handled predictably.

Comment on lines 253 to 257
} catch (JwtException e) {
throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
} catch (BusinessException e) {
throw BusinessException.of(AuthErrorCode.APPLE_LOGIN_ERROR);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

BusinessException을 다시 잡아서 에러 정보가 손실됩니다.

try 블록 내에서 던진 BusinessException (예: NOT_EXISTS_EMAIL, INVALID_TOKEN)이 catch (BusinessException e)에서 잡혀서 APPLE_LOGIN_ERROR로 덮어씌워집니다. 원래의 구체적인 에러 코드가 손실됩니다.

BusinessException은 그대로 다시 던지세요:

 } catch (JwtException e) {
+    log.warn("Apple JWT decoding failed: {}", e.getMessage());
     throw BusinessException.of(AuthErrorCode.INVALID_TOKEN);
 } catch (BusinessException e) {
-    throw BusinessException.of(AuthErrorCode.APPLE_LOGIN_ERROR);
+    throw e;
+} catch (Exception e) {
+    log.error("Apple login error: {}", e.getMessage());
+    throw BusinessException.of(AuthErrorCode.APPLE_LOGIN_ERROR);
 }
🤖 Prompt for AI Agents
In src/main/java/ita/tinybite/domain/auth/service/AuthService.java around lines
253 to 257, a catch (BusinessException e) block is currently intercepting
BusinessExceptions thrown in the try and replacing them with APPLE_LOGIN_ERROR,
which loses the original error code; remove that catch or change it to rethrow
the original BusinessException (throw e) so specific error codes (e.g.,
NOT_EXISTS_EMAIL, INVALID_TOKEN) are preserved, leaving only the JwtException
mapped to INVALID_TOKEN and any other unexpected exceptions handled separately.

@yyytir777 yyytir777 merged commit 729ae0a into develop Dec 16, 2025
1 check passed
@yyytir777 yyytir777 mentioned this pull request Dec 16, 2025
@coderabbitai coderabbitai bot mentioned this pull request Dec 19, 2025
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