Skip to content

Conversation

@msciki7
Copy link
Contributor

@msciki7 msciki7 commented Nov 2, 2025

Summary by CodeRabbit

  • 새 기능

    • 통합 인증 API 추가: 이메일 가입/로그인, 소셜 로그인(구글/카카오/깃허브) 초기화·콜백·완료, 로그아웃·탈퇴, 리프레시 토큰 재발급
  • 개선 사항

    • 소셜 연동 해제(언링크) 기능 추가 및 탈퇴 시 연동 자동 해제
    • 리프레시 토큰 암호화 저장·조건부 갱신 및 HttpOnly 쿠키 처리
    • CORS 허용 출처 업데이트
  • 문서

    • OpenAPI 스키마 보강으로 요청/응답 문서화 개선
  • 테스트

    • 인증·토큰·언링크 관련 단위 테스트 추가·보강
  • 기타

    • 일부 테스트 비활성화 및 Gitignore 설정 변경

@msciki7 msciki7 requested a review from discipline24 as a code owner November 2, 2025 09:33
@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Walkthrough

회원가입·일반 로그인·다중 제공자 OAuth 흐름을 처리하는 신규 AuthController가 추가되고 기존 OauthLoginController가 제거되었습니다. 리프레시 토큰 영속화·재발급 서비스, 토큰 암호화 유틸, OAuth 연결 해제 서비스 및 DTO들의 OpenAPI 주석이 다수 추가되었습니다.

Changes

Cohort / File(s) 변경 요약
컨트롤러 추가/제거
\backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java`, `backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java``
AuthController 신규 추가(회원가입, 일반 로그인, OAuth init/callback, 재발급, 로그아웃, 탈퇴). 기존 OauthLoginController 파일 삭제.
OAuth 어댑터 및 인터페이스
\backend/src/main/java/org/sejongisc/backend/auth/oauth/*``
Google/Kakao/Github UserInfoAdapter에 accessToken 필드·생성자 인자·getter 추가. OauthUserInfo 인터페이스에 getAccessToken() 메서드 추가.
토큰 암호화 & JWT
\backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java`, `.../JwtProvider.java``
AES‑GCM 기반 TokenEncryptor 추가(환경변수 키, encrypt/decrypt). JwtProvider#getExpiration(String) 추가 및 refresh 토큰 암호화 연동.
리프레시 토큰 관리
\backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService*.java`, `backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java``
RefreshTokenService 인터페이스 및 RefreshTokenServiceImpl 구현 추가(재발급, 저장/삭제). RefreshTokenRepository#findByToken 추가.
OAuth 연결 해제 서비스
\backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkService*.java``
OauthUnlinkService 인터페이스 및 OauthUnlinkServiceImpl 추가(카카오/구글/깃허브 unlink HTTP 호출 및 예외 처리). OauthUnlinkException 추가.
엔티티 변경
\backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java``
accessToken, tokenExpiresAt 필드 추가 및 isTokenExpired() 유틸 메서드 추가.
DTO OpenAPI 주석 추가
\backend/src/main/java/org/sejongisc/backend/auth/dto/`, `backend/src/main/java/org/sejongisc/backend/user/dto/``
인증·유저 DTO들에 @Schema 주석 추가(필드 설명·예시).
유저 서비스 확장
\backend/src/main/java/org/sejongisc/backend/user/service/UserService*.java`, `.../UserServiceImpl.java``
getUserById(UUID), deleteUserWithOauth(UUID) 인터페이스/구현 추가: OAuth 계정 복호화·연결 해제 호출 후 사용자 삭제 로직 포함.
보안설정·필터·빈
\backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java`, `.../JwtAuthenticationFilter.java`, `.../RestTemplateConfig.java``
허용 엔드포인트 패턴 확장(/api/auth/, /api/auth/oauth/ 등), CORS origin 변경, RestTemplate 빈 추가, 필터의 제외 패턴 갱신 및 누락 토큰 로깅 보강.
예외·에러코드
\backend/src/main/java/org/sejongisc/backend/auth/exception/OauthUnlinkException.java`, `backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java``
OauthUnlinkException 추가; ErrorCodeEXPIRED_ACCESS_TOKEN 항목 추가.
테스트 변경/추가
\backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java`, `.../RefreshTokenServiceImplTest.java`, `.../OauthUnlinkServiceImplTest.java`, `.../UserServiceImplTest.java`, `.../backtest/*``
컨트롤러 테스트 리네이밍·의존성 갱신, 리프레시/탈퇴 테스트 추가, RefreshTokenServiceImpl·OauthUnlinkServiceImpl 단위테스트 추가, 일부 백테스트 코드 주석 처리(비활성화).
기타 설정 파일
\backend/build.gradle`, `backend/.gitignore`, `backend/src/main/resources/application.yml``
빌드파일 공백 조정, .gitignore에서 application.yml 무시 주석 처리(무시 해제), application.yml에 Hibernate 블록 주석 추가.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthController
    participant OAuthProvider as OAuth Provider
    participant UserService
    participant JwtProvider
    participant RefreshTokenService

    rect rgb(230,245,255)
    Client->>AuthController: GET /api/auth/oauth/{provider}/init
    AuthController->>AuthController: CSRF state 생성/저장
    AuthController-->>Client: provider OAuth URL 반환
    end

    rect rgb(235,255,235)
    Client->>OAuthProvider: 사용자 인증 (브라우저)
    OAuthProvider-->>Client: redirect with code,state
    Client->>AuthController: POST/GET callback (code,state)
    AuthController->>OAuthProvider: 코드로 access token 요청
    OAuthProvider-->>AuthController: access token
    AuthController->>OAuthProvider: access token으로 유저정보 요청
    OAuthProvider-->>AuthController: 유저정보
    AuthController->>UserService: 사용자 조회/생성 (accessToken 암호화 저장)
    UserService-->>AuthController: 사용자 반환
    AuthController->>JwtProvider: access token 생성
    AuthController->>RefreshTokenService: refresh token 발급/저장
    AuthController-->>Client: LoginResponse + HttpOnly refresh cookie
    end
Loading
sequenceDiagram
    participant Client
    participant AuthController
    participant RefreshTokenService
    participant JwtProvider

    Client->>AuthController: POST /api/auth/reissue (refresh cookie)
    AuthController->>RefreshTokenService: reissueTokens(refreshToken)
    RefreshTokenService->>JwtProvider: access token 생성
    alt refresh token near expiry
        RefreshTokenService->>RefreshTokenService: refresh token 재발급 및 저장
        RefreshTokenService-->>AuthController: {accessToken, refreshToken}
    else still valid
        RefreshTokenService-->>AuthController: {accessToken}
    end
    AuthController-->>Client: 새 토큰(s) 반환 (cookie 갱신 가능)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

주의 깊게 검토할 영역:

  • TokenEncryptor.java — 키 길이 검증, AES‑GCM 구현, IV 취급 및 예외 매핑
  • AuthController.java — OAuth 상태 검증, 쿠키(HttpOnly/Secure)·헤더 처리, 예외 경로
  • RefreshTokenServiceImpl.java — 저장/검증 로직 및 재발급 임계값(3일) 처리
  • OauthUnlinkServiceImpl.java — 외부 API 호출(메서드/헤더/바디) 및 에러 처리
  • 테스트 변경 — 기존 OAuth 흐름 제거로 인한 목 설정 및 시나리오 일관성

Possibly related PRs

Suggested reviewers

  • ochanhyeok

Poem

🐇 암호화된 당근 한 알 입에 물고
OAuth 숲길 따라 친구들 만나러 가네
토큰은 숨겨 안전히 저장하고
새로고침 바람 불면 다시 도약하리
탈퇴할 땐 깔끔히 흔적 지우고 휙— 사라진다

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항을 명확하게 반영하고 있습니다. 회원 탈퇴, 리프레시 토큰 구현, 그리고 Swagger 수정이라는 주요 세 가지 작업을 요약하고 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-200-BE-회원-탈퇴-및-refreshtoken-구현

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: 8

Caution

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

⚠️ Outside diff range comments (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)

80-103: 기존 OAuth 계정 accessToken 갱신 누락으로 연동 해제 실패 위험

findByProviderAndProviderUid에서 기존 계정을 찾을 경우 accessToken을 전혀 갱신하지 않습니다. OAuth accessToken은 짧은 주기로 만료되는데, 최초 가입 시점의 토큰만 그대로 저장해 두면 탈퇴 시점에 대부분 만료되어 unlink 호출이 계속 실패합니다. 실제로 deleteUserWithOauth는 DB에 저장된 토큰만 사용하므로, 몇 시간만 지나도 모든 해제 시도가 무력화됩니다. 현재 로그만 남기고 바로 회원을 삭제하기 때문에, 사용자는 소셜 연동이 끊기지 않은 상태로 남게 됩니다.

로그인 시점마다 accessToken을 최신 값으로 저장하도록 즉시 수정해 주세요.

         return oauthAccountRepository
                 .findByProviderAndProviderUid(oauthInfo.getProvider(), providerUid)
-                .map(UserOauthAccount::getUser)
+                .map(account -> {
+                    String freshToken = oauthInfo.getAccessToken();
+                    if (freshToken != null && !freshToken.isBlank()) {
+                        account.setAccessToken(freshToken);
+                        oauthAccountRepository.save(account);
+                    }
+                    return account.getUser();
+                })
                 .orElseGet(() -> {
                     // 새로운 User 생성
                     User newUser = User.builder()
                         ...
🧹 Nitpick comments (5)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)

53-55: 새로운 에러 코드 추가가 적절합니다.

만료된 액세스 토큰에 대한 명시적인 에러 코드 추가는 refresh token 구현과 잘 맞습니다. INVALID_ACCESS_TOKEN과 구분하여 더 구체적인 에러 정보를 제공할 수 있습니다.

선택사항: 포맷 일관성 개선

다른 섹션들은 섹션 사이에 빈 줄을 하나만 사용하는데, 여기는 두 줄이 추가되었습니다. 일관성을 위해 한 줄로 줄이는 것을 고려해보세요.

   EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "엑세스 토큰이 만료되었습니다. 재발급이 필요합니다."),
-
 
   // USER
backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (2)

21-25: 전화번호 형식 검증 추가를 권장합니다.

SignupRequest에서는 전화번호에 대해 정규식 검증(@Pattern(regexp = "^[0-9]{10,11}$"))을 사용하고 있지만, 여기서는 검증이 없습니다. 데이터 일관성을 위해 동일한 형식 검증을 추가하는 것이 좋습니다.

다음과 같이 검증 어노테이션을 추가할 수 있습니다:

+import jakarta.validation.constraints.Pattern;
+
 @Schema(
         description = "변경할 전화번호 (선택 입력, 숫자만 입력)",
         example = "01098765432"
 )
+@Pattern(
+        regexp = "^[0-9]{10,11}$",
+        message = "전화번호는 10~11자리 숫자여야 합니다."
+)
 private String phoneNumber;

27-31: 비밀번호 검증 정책 확인을 권장합니다.

비밀번호 필드에 최소 길이나 복잡도 요구사항이 없습니다. 서비스 레이어에서 비밀번호 강도 검증이 이루어지는지 확인해주세요. 보안을 위해 최소 길이나 복잡도 요구사항을 추가하는 것을 고려할 수 있습니다.

선택적으로 다음과 같은 검증을 추가할 수 있습니다:

+import jakarta.validation.constraints.Size;
+
 @Schema(
         description = "변경할 비밀번호 (선택 입력, 변경 시에만 포함)",
         example = "newpassword123!"
 )
+@Size(min = 8, message = "비밀번호는 최소 8자 이상이어야 합니다.")
 private String password;
backend/src/main/java/org/sejongisc/backend/common/auth/config/RestTemplateConfig.java (1)

10-20: RestTemplate 타임아웃 설정이 적절합니다.

OAuth 연동 해제 작업을 위한 RestTemplate 빈 설정이 적절한 타임아웃 값(연결 3초, 응답 5초)으로 구성되어 있습니다.

프로덕션 환경에서 더 나은 성능과 안정성을 위해 향후 고려할 사항:

  • Apache HttpClient 기반 RestTemplate으로 전환하여 커넥션 풀링 활용
  • 에러 핸들러 커스터마이징 (ResponseErrorHandler)

현재 구현은 기본 기능으로는 충분합니다.

backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService.java (1)

1-20: Refresh Token 서비스 인터페이스가 명확하게 정의되었습니다.

토큰 재발급과 삭제 기능이 적절하게 분리되어 있습니다. Javadoc 문서화도 상세합니다.

선택적 개선 제안: reissueTokens 메서드가 Map<String, String>을 반환하는 대신, 전용 응답 DTO(예: TokenReissueResponse)를 사용하면 타입 안정성이 향상되고 API가 더 명확해집니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8028b8b and 7482fec.

📒 Files selected for processing (39)
  • backend/build.gradle (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (0 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/GithubUserInfoResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleTokenResponse.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleUserInfoResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoTokenResponse.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoUserInfoResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/LoginRequest.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/LoginResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/SignupRequest.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/dto/SignupResponse.java (3 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/oauth/GoogleUserInfoAdapter.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/oauth/OauthUserInfo.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/RestTemplateConfig.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (3 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/service/UserService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (4 hunks)
  • backend/src/test/java/org/sejongisc/backend/auth/controller/AuthControllerTest.java (7 hunks)
  • backend/src/test/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImplTest.java (1 hunks)
  • backend/src/test/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImplTest.java (1 hunks)
  • backend/src/test/java/org/sejongisc/backend/backtest/controller/BacktestControllerTest.java (1 hunks)
  • backend/src/test/java/org/sejongisc/backend/backtest/service/BacktestServiceTest.java (1 hunks)
  • backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (2 hunks)
💤 Files with no reviewable changes (1)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java
🧰 Additional context used
🧬 Code graph analysis (13)
backend/src/main/java/org/sejongisc/backend/auth/dto/LoginResponse.java (3)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (1)
  • Slf4j (25-80)
backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java (1)
  • Test (111-136)
backend/src/test/java/org/sejongisc/backend/auth/service/LoginServiceImplTest.java (1)
  • ExtendWith (33-189)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (2)
backend/src/main/java/org/sejongisc/backend/common/exception/CustomException.java (2)
  • Getter (5-14)
  • CustomException (10-13)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorResponse.java (1)
  • Getter (7-21)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService.java (2)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (1)
  • Override (73-79)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (3)
backend/src/main/java/org/sejongisc/backend/common/config/SwaggerConfig.java (2)
  • Bean (15-39)
  • Configuration (12-40)
backend/src/main/java/org/sejongisc/backend/auth/service/GithubServiceImpl.java (3)
  • Slf4j (19-104)
  • Override (48-78)
  • clientResponse (59-59)
backend/src/test/java/org/sejongisc/backend/auth/service/GithubServiceImplTest.java (2)
  • Test (41-59)
  • Test (61-82)
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (2)
backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java (1)
  • Test (264-271)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
  • Slf4j (25-224)
backend/src/main/java/org/sejongisc/backend/auth/oauth/OauthUserInfo.java (2)
backend/src/main/java/org/sejongisc/backend/auth/service/Oauth2Service.java (3)
  • Oauth2Service (3-12)
  • getAccessToken (8-8)
  • getUserInfo (9-9)
backend/src/main/java/org/sejongisc/backend/auth/service/KakaoServiceImpl.java (3)
  • Slf4j (19-114)
  • Override (45-81)
  • Override (83-113)
backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java (3)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
backend/src/main/java/org/sejongisc/backend/auth/dao/UserOauthAccountRepository.java (1)
  • UserOauthAccountRepository (10-12)
backend/src/main/java/org/sejongisc/backend/user/entity/User.java (1)
  • Entity (12-65)
backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoTokenResponse.java (2)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (1)
  • Getter (10-40)
backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleTokenResponse.java (1)
  • Getter (10-61)
backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (2)
backend/src/main/java/org/sejongisc/backend/auth/dto/SignupRequest.java (1)
  • Getter (10-68)
backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java (1)
  • Getter (10-59)
backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoUserInfoResponse.java (3)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (1)
  • Getter (10-40)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubUserInfoResponse.java (1)
  • Getter (10-54)
backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoTokenResponse.java (1)
  • Getter (10-68)
backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleTokenResponse.java (3)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (1)
  • Getter (10-40)
backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleUserInfoResponse.java (1)
  • Getter (10-47)
backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoTokenResponse.java (1)
  • Getter (10-68)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1)
  • Slf4j (36-502)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (8)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/CustomUserDetailsService.java (1)
  • RequiredArgsConstructor (16-38)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)
  • Slf4j (10-81)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1)
  • Slf4j (19-85)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
  • Slf4j (30-131)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (25-174)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (1)
  • GithubUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GoogleUserInfoAdapter.java (1)
  • GoogleUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (1)
  • KakaoUserInfoAdapter (8-41)
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java

[high] 22-22: Discovered a GitHub OAuth Access Token, posing a risk of compromised GitHub account integrations and data leaks.

(github-oauth)

backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java

[high] 110-110: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 111-111: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 217-217: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 322-322: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (27)
backend/build.gradle (1)

62-63: 포맷팅 변경 승인

ta4j-core 의존성 선언 후 빈 줄 추가는 단순 포맷팅 조정으로 기능적 영향이 없습니다. 코드 가독성에 도움이 됩니다.

backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1)

3-12: Swagger 문서화 추가가 적절합니다.

API 문서화를 위한 Swagger 어노테이션이 명확하게 추가되었습니다. 클래스 설명이 선택적 필드 입력을 잘 설명하고 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoUserInfoResponse.java (1)

5-20: LGTM! Swagger 문서화가 잘 되어 있습니다.

Kakao OAuth 응답 DTO에 대한 Swagger 메타데이터가 체계적으로 추가되었습니다. 다른 OAuth DTO들(GithubUserInfoResponse, GoogleUserInfoResponse)과 일관된 스타일로 작성되어 있으며, 한글 설명과 예시값이 명확하게 제공되어 API 문서의 가독성을 높입니다.

Also applies to: 23-170

backend/src/main/java/org/sejongisc/backend/auth/dto/KakaoTokenResponse.java (1)

5-68: LGTM! 토큰 응답 문서화가 일관성 있게 작성되었습니다.

Kakao 토큰 응답 DTO에 Swagger 어노테이션이 체계적으로 추가되어 다른 OAuth 제공자(Github, Google)의 토큰 응답 DTO와 일관된 문서화 패턴을 유지합니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/LoginRequest.java (1)

3-35: LGTM! 로그인 요청 DTO 문서화가 명확합니다.

Swagger 어노테이션이 기존 validation 제약조건과 일치하게 추가되었으며, requiredMode=REQUIRED@NotBlank와 일관성을 유지합니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/SignupRequest.java (1)

3-68: LGTM! 회원가입 요청 DTO의 문서화가 체계적입니다.

모든 필드에 Swagger 어노테이션이 추가되었으며, requiredMode가 validation 제약조건과 정확히 일치합니다. 정규식 패턴의 멀티라인 포맷팅은 가독성을 높이면서 기능에는 영향을 주지 않습니다.

backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)

215-215: LGTM! OAuth 인터페이스 변경에 따른 테스트 업데이트가 적절합니다.

OauthUserInfo 인터페이스에 getAccessToken() 메서드가 추가됨에 따라 테스트의 익명 구현체에도 해당 메서드를 추가하여 테스트의 정합성을 유지합니다.

Also applies to: 250-250

backend/src/main/java/org/sejongisc/backend/auth/dto/LoginResponse.java (1)

3-67: LGTM! 로그인 응답 DTO의 문서화가 포괄적입니다.

모든 응답 필드에 대해 명확한 설명과 실제적인 예시값이 제공되어 API 문서의 품질을 향상시킵니다. JWT 토큰 예시를 포함한 모든 필드가 일관되게 문서화되어 있습니다.

backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (1)

66-73: 예외 처리가 적절히 구현되어 있습니다.

RefreshTokenServiceImpl.reissueTokens() 메서드의 try-catch 블록에서 getExpiration() 호출 시 발생 가능한 JwtException을 포함한 모든 예외를 처리하고 있습니다(라인 73-75). 유효하지 않거나 만료된 토큰에서 발생하는 예외는 CustomException(ErrorCode.UNAUTHORIZED)로 변환되어 적절히 처리됩니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/GithubUserInfoResponse.java (1)

5-54: Swagger 문서화 추가 확인

API 문서화를 위한 Swagger 애노테이션이 적절하게 추가되었습니다. 설명과 예시가 명확하게 작성되어 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleUserInfoResponse.java (1)

5-46: Swagger 문서화 추가 확인

GitHub DTO와 일관된 방식으로 Swagger 애노테이션이 추가되었습니다.

backend/src/main/java/org/sejongisc/backend/auth/oauth/OauthUserInfo.java (1)

10-10: OAuth 액세스 토큰 노출 메서드 추가 확인

getAccessToken() 메서드가 인터페이스에 추가되었으며, 모든 구현체(Github, Google, Kakao 어댑터)에서 이를 구현하고 있습니다. OAuth 계정 연결 해제 등에 필요한 토큰을 제공하기 위한 적절한 변경입니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/GoogleTokenResponse.java (1)

5-61: Swagger 문서화 추가 확인

다른 OAuth 토큰 응답 DTO와 일관되게 Swagger 애노테이션이 추가되었습니다.

backend/src/main/java/org/sejongisc/backend/user/service/UserService.java (2)

18-18: 사용자 조회 메서드 추가 확인

UUID로 사용자를 조회하는 표준 메서드가 추가되었습니다.


20-20: OAuth 연동 회원 탈퇴 메서드 추가 확인

OAuth 계정 연결 해제와 함께 사용자를 삭제하는 메서드가 추가되었습니다. 이는 회원 탈퇴 기능 구현의 핵심 부분으로 적절합니다.

backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (2)

11-16: 액세스 토큰 필드 추가 확인

다른 OAuth 어댑터(Github, Google)와 일관되게 accessToken 필드가 추가되고 생성자가 업데이트되었습니다.


37-40: getAccessToken() 구현 확인

OauthUserInfo 인터페이스의 새로운 메서드를 올바르게 구현했습니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/GithubTokenResponse.java (1)

5-5: Swagger 문서화 추가가 잘 되었습니다.

API 문서화를 위한 OpenAPI 애너테이션이 적절하게 추가되었습니다. 각 필드에 대한 설명과 예시가 명확하게 작성되었습니다.

참고: 정적 분석 도구가 Line 22의 예시 토큰을 실제 GitHub OAuth 토큰으로 오탐지하고 있으나, 이는 문서화 목적의 예시 값이므로 무시해도 됩니다.

Also applies to: 14-39

backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkService.java (1)

1-22: OAuth 연동 해제 인터페이스가 잘 정의되었습니다.

세 가지 OAuth 제공자(카카오, 구글, 깃허브)에 대한 연동 해제 메서드가 명확하게 정의되었습니다. Javadoc 주석도 적절하게 작성되었습니다.

backend/src/main/java/org/sejongisc/backend/auth/dto/SignupResponse.java (2)

4-4: Swagger 문서화가 체계적으로 추가되었습니다.

회원가입 응답 DTO에 대한 OpenAPI 애너테이션이 잘 추가되었습니다. 각 필드의 설명과 예시가 명확하며, 날짜 형식 예시도 적절합니다.

Also applies to: 13-56


57-65: 생성자 포맷팅 개선이 좋습니다.

필드 할당 시 공백을 추가하여 가독성이 향상되었습니다.

backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (2)

39-52: 인증 제외 패턴이 적절하게 확장되었습니다.

와일드카드 패턴(/api/auth/login/**)을 사용하여 여러 개별 경로를 단순화한 것이 좋습니다. 새로운 엔드포인트(/api/auth/logout, /api/auth/reissue)가 적절하게 추가되었습니다.


76-82: 토큰 검증 실패 시 로깅 추가가 유용합니다.

토큰이 없거나 유효하지 않은 경우에 대한 로깅이 추가되어 디버깅과 모니터링에 도움이 됩니다.

backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (2)

39-52: 공개 엔드포인트 설정이 일관성 있게 업데이트되었습니다.

JwtAuthenticationFilter의 EXCLUDE_PATTERNS와 일치하도록 보안 설정이 업데이트되었습니다. 와일드카드 패턴 사용으로 코드가 간결해졌습니다.


68-68: 프론트엔드 포트 변경이 의도된 변경임을 확인했습니다. 프론트엔드에서 실제로 포트 8082를 사용하는지 확인해주세요.

Git 커밋 히스토리에서 최근 커밋 7482fec [BE] SISC1-200 [FEAT] Swagger 설명 추가 및 CORS 설정 수정에 따라 CORS 허용 오리진이 http://localhost:5173에서 http://localhost:8082로 변경되었으며, 이는 SISC1-200 티켓 작업의 일부입니다. 변경 사항이 문서화된 의도적인 수정이므로, 프론트엔드 프로젝트에서 실제로 포트 8082를 사용하도록 설정되어 있는지만 확인하면 됩니다.

backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (1)

11-11: Access Token 저장 기능이 적절하게 추가되었습니다.

OAuth 연동 해제 기능을 지원하기 위해 accessToken 필드와 getter 메서드가 추가되었습니다. OauthUserInfo 인터페이스의 요구사항을 충족하며, 다른 어댑터들과 일관성 있는 패턴을 따릅니다.

Also applies to: 13-16, 34-37

backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)

30-30: Swagger API 문서화가 상세하게 추가되었습니다.

각 엔드포인트에 대한 설명, 응답 코드, 예시 페이로드가 포괄적으로 작성되었습니다. API 문서의 품질이 크게 향상되었습니다.

Also applies to: 35-69, 93-128

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: 10

♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)

35-46: 🔴 이전 리뷰의 치명적 버그가 여전히 수정되지 않았습니다!

Line 38의 new HttpEntity<>(headers)는 여전히 잘못된 코드입니다. HttpEntity의 단일 인자 생성자는 HttpEntity(T body)이므로, headers 객체가 요청 본문으로 전달되고 실제 HTTP 헤더는 비어있게 됩니다. 따라서 Authorization 헤더가 누락되어 Kakao API 호출이 401 에러로 실패합니다.

다음과 같이 수정하세요:

-            HttpEntity<Void> request = new HttpEntity<>(headers);
+            HttpEntity<Void> request = new HttpEntity<>(null, headers);
🧹 Nitpick comments (7)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1)

16-27: 문자 인코딩 명시 추천
Line 16과 Line 27의 token.getBytes()new String(...)은 플랫폼 기본 인코딩에 의존합니다. 서버가 JVM 기본값을 바꾸면 암호문 검증이 깨질 수 있으니 UTF-8과 같이 명시적인 StandardCharsets.UTF_8을 사용하세요.

다음과 같이 교체하면 됩니다:

-            return Base64.getEncoder().encodeToString(cipher.doFinal(token.getBytes()));
+            return Base64.getEncoder().encodeToString(cipher.doFinal(token.getBytes(StandardCharsets.UTF_8)));
-            return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedToken)));
+            return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedToken)), StandardCharsets.UTF_8);
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (4)

3-3: Spring의 @transactional 사용을 권장합니다.

jakarta.transaction.Transactional 대신 org.springframework.transaction.annotation.Transactional을 사용하는 것이 좋습니다. Spring의 @Transactional은 propagation, isolation level, rollback rules 등 더 많은 기능을 제공하며, Spring 애플리케이션에서는 표준으로 사용됩니다.

다음과 같이 import를 변경하세요:

-import jakarta.transaction.Transactional;
+import org.springframework.transaction.annotation.Transactional;

32-33: UUID 파싱 예외 처리를 개선하세요.

UUID.fromString()IllegalArgumentException을 던질 수 있습니다. 현재는 일반 예외 핸들러에서 잡히지만, 더 명확한 에러 메시지를 위해 명시적으로 처리하는 것을 고려하세요.

 try {
     // refreshToken에서 userId 추출
-    UUID userId = UUID.fromString(jwtProvider.getUserIdFromToken(refreshToken));
+    String userIdStr = jwtProvider.getUserIdFromToken(refreshToken);
+    UUID userId;
+    try {
+        userId = UUID.fromString(userIdStr);
+    } catch (IllegalArgumentException e) {
+        log.warn("Invalid UUID format in token: {}", e.getMessage());
+        throw new CustomException(ErrorCode.UNAUTHORIZED);
+    }

56-62: 매직 넘버를 상수로 추출하세요.

Line 57의 3L * 24 * 60 * 60 * 1000은 매직 넘버입니다. 가독성과 유지보수성을 위해 클래스 상수로 추출하거나, 더 나아가 application.properties에서 설정 가능하도록 하는 것이 좋습니다.

클래스 상수로 추출:

+private static final long REFRESH_TOKEN_RENEWAL_THRESHOLD_MILLIS = 3L * 24 * 60 * 60 * 1000; // 3 days
+
 @Slf4j
 @Service
 @RequiredArgsConstructor
 public class RefreshTokenServiceImpl implements RefreshTokenService {

그리고 사용:

-if (remainingMillis < (3L * 24 * 60 * 60 * 1000)) {
+if (remainingMillis < REFRESH_TOKEN_RENEWAL_THRESHOLD_MILLIS) {

또는 설정 가능한 값으로:

@Value("${jwt.refresh-token.renewal-threshold-days:3}")
private long renewalThresholdDays;

// 사용 시
if (remainingMillis < (renewalThresholdDays * 24 * 60 * 60 * 1000)) {

47-62: 토큰 갱신 전략이 잘 구현되었습니다.

만료 임박 시 refresh token을 자동으로 갱신하는 로직은 보안 모범 사례입니다. 다만, Line 52의 getExpiration() 호출은 Line 33에서 이미 파싱한 토큰을 다시 파싱하게 됩니다. 성능이 중요한 경우 토큰 파싱을 한 번만 수행하도록 최적화할 수 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (2)

40-41: RestTemplate 호출에 타임아웃 설정 권장

외부 API 호출에 타임아웃이 설정되어 있지 않으면 네트워크 문제 발생 시 무한정 대기할 수 있습니다. RestTemplateConfig에서 connection timeout과 read timeout을 설정하는 것을 권장합니다.

RestTemplateConfig에서 타임아웃을 설정하세요:

@Bean
public RestTemplate restTemplate() {
    HttpComponentsClientHttpRequestFactory factory = 
        new HttpComponentsClientHttpRequestFactory();
    factory.setConnectTimeout(5000);  // 5초
    factory.setReadTimeout(5000);     // 5초
    return new RestTemplate(factory);
}

Also applies to: 52-53, 70-75


42-42: 응답 본문 로깅 시 민감 정보 노출 가능성

OAuth unlink API의 응답 본문에 민감한 정보가 포함될 수 있습니다. 프로덕션 환경에서는 전체 응답 본문을 로깅하지 않거나, 민감 정보를 마스킹하는 것을 권장합니다.

로그 레벨을 조정하거나 응답 본문 로깅을 제거하세요:

-            log.info("Kakao Unlink 성공: {}", response.getBody());
+            log.info("Kakao Unlink 성공: status={}", response.getStatusCode());

Also applies to: 54-54, 76-76

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7482fec and 4067e0f.

📒 Files selected for processing (10)
  • backend/.gitignore (1 hunks)
  • backend/build.gradle (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/build.gradle
  • backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
  • backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java
🧰 Additional context used
🧬 Code graph analysis (3)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (5)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1)
  • Slf4j (36-502)
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java (3)
  • RefreshTokenRepository (9-14)
  • deleteByUserId (13-13)
  • findByUserId (11-11)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (3)
  • Slf4j (25-80)
  • Override (36-71)
  • Override (73-79)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (1)
  • createRefreshToken (53-64)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (7)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)
  • Slf4j (10-81)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1)
  • Slf4j (20-87)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-182)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
  • Slf4j (30-131)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (1)
  • GithubUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GoogleUserInfoAdapter.java (1)
  • GoogleUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (1)
  • KakaoUserInfoAdapter (8-41)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-182)
🪛 ast-grep (0.39.6)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java

[warning] 13-13: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Cipher.getInstance(ALGORITHM)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA

(desede-is-deprecated-java)


[warning] 24-24: Triple DES (3DES or DESede) is considered deprecated. AES is the recommended cipher. Upgrade to use AES.
Context: Cipher.getInstance(ALGORITHM)
Note: [CWE-326]: Inadequate Encryption Strength [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://find-sec-bugs.github.io/bugs.htm#TDES_USAGE
- https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA

(desede-is-deprecated-java)


[warning] 13-13: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Cipher.getInstance(ALGORITHM)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html

(use-of-aes-ecb-java)


[warning] 24-24: Use of AES with ECB mode detected. ECB doesn't provide message confidentiality and is not semantically secure so should not be used. Instead, use a strong, secure cipher: Cipher.getInstance("AES/CBC/PKCS7PADDING"). See https://owasp.org/www-community/Using_the_Java_Cryptographic_Extensions for more information.
Context: Cipher.getInstance(ALGORITHM)
Note: [CWE-327]: Use of a Broken or Risky Cryptographic Algorithm [OWASP A03:2017]: Sensitive Data Exposure [OWASP A02:2021]: Cryptographic Failures [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
- https://googleprojectzero.blogspot.com/2022/10/rc4-is-still-considered-harmful.html

(use-of-aes-ecb-java)

🔇 Additional comments (7)
backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java (3)

8-8: LGTM! 필요한 import 추가

LocalDateTime import가 새로운 tokenExpiresAt 필드에 적절하게 사용되고 있습니다.


42-43: LGTM! 토큰 만료 시간 필드 추가

이전 리뷰에서 권장된 토큰 만료 시간 추적 기능이 올바르게 구현되었습니다.


39-40: 암호화는 이미 구현되어 있습니다 - 리뷰 코멘트가 부정확합니다

이전 리뷰에서 지적한 토큰 길이 문제는 해결되었으며, OAuth 액세스 토큰 암호화도 이미 구현되어 있습니다. TokenEncryptor를 통해 저장 전 AES 암호화, 조회 시 자동 복호화가 이루어지고 있습니다.

유일한 개선 사항: TokenEncryptor.java의 SECRET_KEY가 여전히 하드코딩되어 있습니다. 환경 변수로 관리하도록 수정하세요.

Likely an incorrect or invalid review comment.

backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (3)

72-77: 예외 처리 구조가 잘 설계되었습니다.

커스텀 예외는 그대로 전파하고, 예상치 못한 예외는 적절히 래핑하여 일관된 에러 응답을 제공합니다. 로깅도 적절하게 구현되어 있습니다.


35-41: 토큰 검증 로직이 올바르게 구현되었습니다.

DB에 저장된 refresh token과 요청된 token을 비교하는 로직이 정확합니다. findByUserId를 사용하는 것도 적절하며, 토큰 탈취를 방지할 수 있습니다.


80-85: 삭제 메서드가 올바르게 구현되었습니다.

트랜잭션 어노테이션과 로깅이 적절하게 적용되어 있으며, 회원 탈퇴 시 refresh token을 제거하는 로직이 정확합니다.

backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)

10-30: 클래스 구조 및 의존성 주입이 올바르게 구현되었습니다.

Spring의 표준 패턴을 잘 따르고 있으며, 필요한 의존성들이 적절히 주입되고 있습니다.

# Conflicts:
#	backend/src/main/resources/application.yml
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: 3

🧹 Nitpick comments (2)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (2)

17-24: 이전 보안 이슈 해결 확인

이전 리뷰에서 지적된 하드코딩된 비밀키 문제가 환경변수로 적절히 개선되었습니다. 키 길이 검증과 명확한 에러 메시지도 잘 구현되었습니다.

선택적 개선사항: 현재는 암호화/복호화 호출마다 loadKey()를 실행하여 환경변수를 읽고 검증합니다. 빈번한 호출 시 오버헤드가 발생할 수 있으므로, 필요하다면 정적 초기화 블록에서 키를 한 번만 로드하고 캐싱하는 방식을 고려할 수 있습니다.


26-52: AES-GCM 모드 적용 확인 및 입력 검증 추가 필요

이전 리뷰에서 지적된 ECB 모드 보안 취약점이 AES/GCM/NoPadding으로 적절히 개선되었습니다. IV 생성과 암호문 결합 로직도 올바르게 구현되었습니다.

개선 권장사항:

  1. 입력 검증 추가: token 파라미터에 대한 null/빈 문자열 검증이 없어 NPE 발생 시 원인 파악이 어려울 수 있습니다.

  2. 예외 처리 구체화: 모든 예외를 RuntimeException으로 래핑하면 키 로드 실패, 암호화 실패 등 구체적인 원인을 구분하기 어렵습니다.

다음과 같이 개선할 수 있습니다:

 public static String encrypt(String token) {
+    if (token == null || token.isEmpty()) {
+        throw new IllegalArgumentException("암호화할 토큰이 null이거나 비어있습니다.");
+    }
     try {
         SecretKeySpec key = loadKey();
         // ... 나머지 로직
-    } catch (Exception e) {
-        throw new RuntimeException("Token encryption failed", e);
+    } catch (IllegalStateException e) {
+        // 키 로드 실패는 그대로 전파
+        throw e;
+    } catch (Exception e) {
+        throw new RuntimeException("토큰 암호화에 실패했습니다.", e);
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4067e0f and 0940c0a.

📒 Files selected for processing (8)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/exception/OauthUnlinkException.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1 hunks)
  • backend/src/main/resources/application.yml (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/src/main/resources/application.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java
🧰 Additional context used
🧬 Code graph analysis (3)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (2)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (3)
  • Component (18-99)
  • PostConstruct (31-35)
  • createRefreshToken (53-64)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-182)
backend/src/main/java/org/sejongisc/backend/auth/exception/OauthUnlinkException.java (1)
  • OauthUnlinkException (3-11)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (8)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/CustomUserDetailsService.java (1)
  • RequiredArgsConstructor (16-38)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)
  • Slf4j (14-104)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1)
  • Slf4j (20-99)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-182)
backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
  • Slf4j (30-131)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (1)
  • GithubUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GoogleUserInfoAdapter.java (1)
  • GoogleUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (1)
  • KakaoUserInfoAdapter (8-41)
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java

[high] 110-110: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 111-111: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 217-217: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 324-324: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (11)
backend/src/main/java/org/sejongisc/backend/auth/exception/OauthUnlinkException.java (1)

3-11: LGTM!

표준 Java 예외 클래스 패턴을 잘 따르고 있으며, OAuth 연동 해제 실패 시나리오를 명확하게 표현하는 예외 클래스입니다.

backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenService.java (1)

6-21: LGTM!

인터페이스 설계가 명확하고 JavaDoc 문서화도 잘 되어 있습니다. 메서드 시그니처가 직관적이며 책임이 잘 분리되어 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)

38-55: unlinkKakao 메서드 구현이 올바릅니다.

이전 리뷰에서 지적된 HttpEntity 생성 및 입력 검증 문제가 모두 수정되었습니다. 현재 코드는 헤더를 올바르게 설정하고, 예외 처리도 적절하게 구현되어 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (2)

29-79: LGTM! 이전 리뷰 사항이 반영되었습니다.

@Transactional 어노테이션이 올바르게 추가되어 데이터 일관성이 보장됩니다. 토큰 검증 로직, 조건부 리프레시 토큰 갱신(3일 임박 시), 에러 핸들링 모두 잘 구현되어 있습니다.


81-97: LGTM!

deleteByUserIdsaveOrUpdateToken 메서드 모두 트랜잭션 처리가 적절하며, saveOrUpdateTokenifPresentOrElse 패턴을 사용한 upsert 로직이 간결하고 명확합니다.

backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (6)

93-98: LGTM!

회원가입 엔드포인트가 적절하게 구현되어 있으며, Swagger 문서화도 잘 되어 있습니다.


122-140: LGTM!

일반 로그인 엔드포인트가 올바르게 구현되어 있습니다. HttpOnly, Secure, SameSite 쿠키 설정이 보안 모범 사례를 따르고 있으며, Authorization 헤더도 적절하게 설정되어 있습니다.


237-312: LGTM! 이전 리뷰 사항이 반영되었습니다.

Line 286에서 refreshTokenService.saveOrUpdateToken을 호출하여 OAuth 로그인 시에도 refresh 토큰을 서버에 저장하도록 수정되었습니다. 이제 OAuth 사용자도 /api/auth/reissue 엔드포인트를 통해 토큰 재발급을 받을 수 있습니다.


340-381: LGTM!

토큰 재발급 엔드포인트가 잘 구현되어 있습니다. 조건부 refresh 토큰 갱신 로직과 에러 처리가 적절합니다.


473-502: LGTM! 이전 리뷰 사항이 반영되었습니다.

Line 500에서 deleteCookie를 응답 헤더에 추가하여 회원 탈퇴 시 클라이언트의 refresh 쿠키가 제대로 삭제되도록 수정되었습니다. 또한 Line 488에서 refreshTokenService.deleteByUserId를 호출하여 서버 측 토큰도 정리하고 있습니다.


110-111: 정적 분석 경고는 무시하셔도 됩니다.

Gitleaks가 "Generic API Key"로 탐지한 항목들은 Swagger 문서화를 위한 예시 JWT 토큰 문자열입니다. 실제 시크릿이 아니므로 보안 문제가 없습니다.

Also applies to: 217-217, 324-324

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: 1

🧹 Nitpick comments (6)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (2)

26-59: 암호화 로직이 올바르게 구현되었습니다.

AES/GCM 암호화, IV 생성 및 연결, 예외 처리가 적절합니다. SecureRandom.getInstanceStrong() 실패 시 일반 SecureRandom()으로 폴백하는 것도 가용성 측면에서 합리적입니다.

선택사항: Lines 36-40의 generic Exception 캐치를 NoSuchAlgorithmException으로 구체화하면 디버깅 시 더 명확합니다:

 byte[] iv = new byte[GCM_IV_LENGTH];
 SecureRandom random;
 try {
     random = SecureRandom.getInstanceStrong();
-} catch (Exception ex) {
+} catch (NoSuchAlgorithmException ex) {
     random = new SecureRandom();
 }

61-97: 복호화 로직과 예외 처리가 훌륭합니다.

입력 검증, GCM 인증 태그 실패를 별도로 처리하는 예외 핸들링(AEADBadTagExceptionSecurityException), 그리고 일관된 한국어 오류 메시지가 모두 우수합니다.

선택사항: Line 70의 길이 검증을 더 구체화하면 오류 메시지가 명확해집니다:

-if (decoded.length < GCM_IV_LENGTH) {
-    throw new IllegalArgumentException("암호화된 토큰의 길이가 올바르지 않습니다.");
+// GCM은 최소 IV(12) + 태그(16) = 28바이트 필요
+if (decoded.length < GCM_IV_LENGTH + 16) {
+    throw new IllegalArgumentException("암호화된 토큰의 길이가 올바르지 않습니다. 최소 28바이트 필요.");
 }
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (4)

43-55: HttpEntity 생성자를 명시적인 형태로 개선하고 로깅 레벨을 조정하세요.

Line 46에서 new HttpEntity<>(headers) 형태는 HttpHeaders가 MultiValueMap을 구현하므로 기술적으로는 작동하지만, 이전 리뷰에서 critical로 지적되었던 부분입니다. 명확성과 안정성을 위해 2인자 형태 new HttpEntity<>(null, headers)를 사용하는 것이 권장됩니다.

또한 예외를 throw하는 상황에서는 log.warn 대신 log.error를 사용하는 것이 더 적절합니다.

다음 diff를 적용하세요:

         try{
             HttpHeaders headers = new HttpHeaders();
             headers.setBearerAuth(accessToken);
-            HttpEntity<Void> request = new HttpEntity<>(headers);
+            HttpEntity<Void> request = new HttpEntity<>(null, headers);

             ResponseEntity<String> response =
                     restTemplate.exchange(kakaoUnlinkUrl, HttpMethod.POST, request, String.class);
             log.info("Kakao Unlink 성공: {}", response.getBody());
         }catch (Exception e){
-            log.warn("Kakao Unlink 실패: {}", e.getMessage());
+            log.error("Kakao Unlink 실패: {}", e.getMessage(), e);
             throw new OauthUnlinkException("Kakao 연동 해제 실패", e);
         }

63-76: 로깅 레벨을 error로 변경하세요.

이전 리뷰에서 지적된 URL 인코딩 및 에러 메시지 이슈가 잘 수정되었습니다. 다만 예외를 throw하는 상황에서는 log.warn 대신 log.error를 사용하고 스택 트레이스도 함께 로깅하는 것이 디버깅에 유용합니다.

다음 diff를 적용하세요:

         } catch (Exception e) {
-            log.warn("Google unlink 실패: {}", e.getMessage());
+            log.error("Google unlink 실패: {}", e.getMessage(), e);
             throw new OauthUnlinkException("Google 연동 해제 실패", e);
         }

84-103: 로깅 레벨을 개선하고, 선택적으로 URL 구성 방식을 고려하세요.

이전 리뷰에서 지적된 에러 메시지 이슈가 잘 수정되었고, HttpEntity도 올바른 2인자 형태로 구성되어 있습니다.

다만 다음 개선사항을 고려해주세요:

  1. 예외를 throw하는 상황에서는 log.error를 사용하고 스택 트레이스도 로깅
  2. (선택사항) Line 93의 String replace 대신 UriComponentsBuilder를 사용하면 더 안전함

필수: 로깅 레벨 개선

         } catch (Exception e) {
-            log.warn("GitHub unlink 실패: {}", e.getMessage());
+            log.error("GitHub unlink 실패: {}", e.getMessage(), e);
             throw new OauthUnlinkException("GitHub 연동 해제 실패", e);
         }

선택사항: URL 구성 개선

+            String url = UriComponentsBuilder.fromHttpUrl(githubUnlinkUrl)
+                    .buildAndExpand(githubClientId)
+                    .toUriString();
+
             ResponseEntity<String> response = restTemplate.exchange(
-                    githubUnlinkUrl.replace("{client_id}", githubClientId),
+                    url,
                     HttpMethod.DELETE,
                     request,
                     String.class
             );

51-54: 예외 처리 범위를 더 구체적으로 좁힐 수 있습니다.

현재 모든 메서드에서 Exception을 catch하고 있는데, RestClientException을 사용하면 네트워크/HTTP 관련 예외를 더 명확히 처리할 수 있습니다. 다만 현재 구현도 실용적이므로 선택적으로 개선하세요.

+import org.springframework.web.client.RestClientException;
+
-        }catch (Exception e){
+        }catch (RestClientException e){
             log.error("Kakao Unlink 실패: {}", e.getMessage(), e);
             throw new OauthUnlinkException("Kakao 연동 해제 실패", e);
         }

동일한 패턴을 unlinkGoogleunlinkGithub 메서드에도 적용할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0940c0a and 9a67486.

📒 Files selected for processing (2)
  • backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-182)
backend/src/main/java/org/sejongisc/backend/auth/exception/OauthUnlinkException.java (1)
  • OauthUnlinkException (3-11)
🔇 Additional comments (2)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1)

1-16: 보안 모범 사례를 잘 따르고 있습니다.

AES/GCM/NoPadding 모드 사용과 적절한 IV 길이(12바이트), 인증 태그 길이(128비트) 설정이 올바릅니다. 환경변수를 통한 키 관리도 이전 리뷰의 보안 우려사항을 적절히 해결했습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)

14-35: 클래스 구조 및 의존성 주입이 적절합니다.

서비스 구현체의 구조가 깔끔하고, 필요한 설정 값들이 적절히 주입되고 있습니다.

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다

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: 1

♻️ Duplicate comments (3)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (2)

284-286: OAuth 로그인 시 Refresh Token 저장 확인됨

Line 286에서 refreshTokenService.saveOrUpdateToken()을 호출하여 refresh token을 DB에 저장하고 있습니다. 이전 리뷰에서 지적된 문제가 해결되었습니다.


499-500: 회원 탈퇴 시 쿠키 삭제 헤더 추가 확인됨

Line 500에서 SET_COOKIE 헤더를 추가하여 클라이언트의 refresh 쿠키를 삭제하도록 구현되어 있습니다. 이전 리뷰에서 지적된 문제가 해결되었습니다.

backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1)

24-30: 암호화 키 검증 및 기본값 개선 필요

두 가지 문제가 있습니다:

  1. 키 길이 검증 오류: Line 25에서 key.length() != 16은 문자 개수를 확인하지만, UTF-8 인코딩 후 실제 바이트 길이는 다를 수 있습니다. 멀티바이트 문자(예: 한글)가 포함되면 16문자여도 16바이트가 아닐 수 있습니다.

  2. 안전하지 않은 기본값: 기본값 "mySecretKey1234"는 15자이며 검증에 실패합니다. 더 중요한 것은, 프로덕션에서 기본값을 사용하면 심각한 보안 취약점이 됩니다.

다음과 같이 수정하는 것을 권장합니다:

-    public TokenEncryptor(@Value("${TOKEN_ENCRYPTION_KEY:mySecretKey1234}") String key) {
-        if (key == null || key.length() != 16) {
+    public TokenEncryptor(@Value("${TOKEN_ENCRYPTION_KEY:}") String key) {
+        if (key == null || key.isEmpty()) {
+            throw new IllegalStateException(
+                    "TOKEN_ENCRYPTION_KEY 환경변수가 설정되지 않았습니다. 반드시 16바이트 키를 설정해야 합니다.");
+        }
+        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+        if (keyBytes.length != 16) {
             throw new IllegalStateException(
-                    "유효한 16바이트 토큰 암호화 키가 설정되지 않았습니다. 환경변수 TOKEN_ENCRYPTION_KEY를 확인하세요.");
+                    "TOKEN_ENCRYPTION_KEY는 정확히 16바이트여야 합니다. 현재: " + keyBytes.length + "바이트");
         }
-        this.secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM);
+        this.secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
     }
🧹 Nitpick comments (3)
backend/src/main/resources/application.yml (1)

7-8: YAML 설정과 Java 코드 설정 간의 중복 검토 필요

hibernate.ddl-auto: update 설정이 application.yml에서 주석 처리되었으나, PrimaryDataSourceConfig.java(라인 48)의 Java 코드에서 동일한 속성이 여전히 "update"로 명시적으로 설정되어 있습니다. 이는 설정 관리의 일관성을 해칠 수 있습니다.

설정의 단일 소스(Single Source of Truth) 원칙에 따라, YAML 또는 Java 코드 중 하나에서만 관리하도록 통일하고 다른 곳의 설정을 제거하거나 명확한 주석으로 의도를 문서화하시기 바랍니다.

backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (2)

165-170: switch 표현식에서 default 케이스 처리 개선 권장

Line 165-170의 switch 표현식에서 지원하지 않는 provider에 대해 경고 로그만 남기고 있습니다. 하지만 이 경우 연동 해제가 실패했음에도 사용자 삭제가 진행될 수 있습니다.

지원하지 않는 provider인 경우 예외를 발생시켜 트랜잭션을 롤백하거나, 최소한 연동 해제 실패를 명확히 처리하는 것을 권장합니다.

다음과 같이 수정하는 것을 고려해 보세요:

                 // Kakao / Google / GitHub 연동 해제 서비스 연결
                 switch (provider.toLowerCase()) {
                     case "kakao" -> oauthUnlinkService.unlinkKakao(accessToken);
                     case "google" -> oauthUnlinkService.unlinkGoogle(accessToken);
                     case "github" -> oauthUnlinkService.unlinkGithub(accessToken);
-                    default -> log.warn("지원하지 않는 provider: {}", provider);
+                    default -> throw new IllegalStateException("지원하지 않는 OAuth provider: " + provider);
                 }

174-175: 주석 처리된 코드 제거 권장

Line 174-175의 주석 처리된 Refresh Token 삭제 로직은 실제로 AuthController의 withdraw 엔드포인트(Line 488)에서 refreshTokenService.deleteByUserId(userId)로 이미 처리되고 있습니다.

혼란을 방지하기 위해 이 주석을 제거하는 것을 권장합니다.

-        // Refresh Token (추후 구현 시 삭제)
-        //refreshTokenRepository.deleteByUserId(userId);
-
         // User 삭제 (연관된 OAuthAccount는 Cascade로 자동 삭제)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fbbe53 and bcf679b.

📒 Files selected for processing (6)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (3 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (4 hunks)
  • backend/src/main/resources/application.yml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
backend/src/main/resources/application.yml (1)
backend/src/main/java/org/sejongisc/backend/common/config/PrimaryDataSourceConfig.java (1)
  • Configuration (22-83)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (3)
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java (2)
  • RefreshTokenRepository (9-14)
  • deleteByUserId (13-13)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (2)
  • Override (36-71)
  • Override (73-79)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (8)
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (1)
  • Slf4j (21-104)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Slf4j (26-183)
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java (1)
  • Slf4j (14-104)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GithubUserInfoAdapter.java (1)
  • GithubUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/GoogleUserInfoAdapter.java (1)
  • GoogleUserInfoAdapter (8-38)
backend/src/main/java/org/sejongisc/backend/auth/oauth/KakaoUserInfoAdapter.java (1)
  • KakaoUserInfoAdapter (8-41)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
  • Slf4j (25-224)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (1)
  • Slf4j (25-80)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (2)
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (1)
  • Component (19-114)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
  • Entity (7-22)
🪛 Gitleaks (8.28.0)
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java

[high] 110-110: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 111-111: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 217-217: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 324-324: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (17)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (4)

4-5: 새로운 의존성 통합 확인됨

OAuth 연동 해제를 위한 OauthUnlinkService와 토큰 암호화를 위한 TokenEncryptor 의존성이 적절하게 추가되었습니다. 이들은 회원 탈퇴 및 OAuth 토큰 관리 플로우에서 올바르게 활용되고 있습니다.

Also applies to: 34-34, 36-36


95-96: OAuth 액세스 토큰 암호화 적용 확인됨

OAuth 액세스 토큰을 저장하기 전에 TokenEncryptor를 사용하여 암호화하고 있어, 민감한 토큰 정보를 안전하게 보호하고 있습니다. 이는 보안 모범 사례에 부합합니다.

Also applies to: 101-101


138-143: getUserById 메서드 구현 확인됨

사용자 ID로 사용자를 조회하는 간단하고 명확한 구현입니다. 사용자가 없을 경우 적절한 예외를 발생시키고 있습니다.


152-153: Lazy 로딩 강제 초기화의 필요성 검토 필요

Line 153에서 user.getOauthAccounts().size()를 호출하여 Lazy 로딩을 강제로 초기화하고 있습니다. 클래스 레벨에 @Transactional이 적용되어 있으므로, 트랜잭션 내에서 실행되며 Lazy 로딩이 정상적으로 작동해야 합니다.

이 명시적 초기화가 특정 환경에서 필요한 것인지 확인해 주세요. 일반적으로 트랜잭션 컨텍스트 내에서는 불필요할 수 있습니다.

backend/src/main/java/org/sejongisc/backend/common/auth/jwt/JwtProvider.java (3)

9-9: TokenEncryptor 의존성 통합 확인됨

@RequiredArgsConstructor를 사용하여 TokenEncryptor 의존성을 주입받도록 변경되었습니다. 이는 Lombok의 생성자 주입 패턴을 따르며 깔끔한 구현입니다.

Also applies to: 20-20, 23-23


62-70: Refresh Token 암호화 적용 확인됨

Refresh Token 생성 시 TokenEncryptor를 사용하여 암호화한 후 반환하고 있습니다. 이는 데이터베이스와 쿠키에 저장되는 refresh token의 보안을 강화합니다.


72-79: 토큰 만료 시간 추출 메서드 추가 확인됨

JWT에서 만료 시간을 추출하는 getExpiration 메서드가 추가되었습니다. 이는 RefreshTokenServiceImpl에서 refresh token 재발급 시점을 판단하는 데 사용되고 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java (3)

31-84: Refresh Token 재발급 로직에서 @transactional 적용 확인됨

Line 32에 @Transactional 어노테이션이 적용되어 있어, DB 업데이트 작업(Line 65-66)의 원자성이 보장됩니다. 다만 Line 45의 토큰 검증 로직 오류는 반드시 수정되어야 합니다.


86-91: deleteByUserId 메서드 구현 확인됨

사용자 ID로 refresh token을 삭제하는 간단하고 명확한 구현입니다. 로그아웃 및 회원 탈퇴 플로우에서 적절하게 사용되고 있습니다.


93-102: saveOrUpdateToken 메서드 구현 확인됨

기존 토큰이 있으면 업데이트하고, 없으면 새로 생성하는 upsert 패턴이 적절하게 구현되어 있습니다. ifPresentOrElse를 사용한 간결한 구현입니다.

backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java (2)

32-64: AES/GCM 암호화 구현 확인됨

보안 모범 사례에 따라 AES/GCM/NoPadding 모드를 사용하고 있으며, 다음 사항이 적절하게 구현되어 있습니다:

  • 12바이트 랜덤 IV 생성 (Line 39-46)
  • 128비트 인증 태그 (Line 48)
  • IV를 암호문 앞에 결합 (Line 54-57)
  • 적절한 입력 검증 (Line 33-35)

66-102: 복호화 구현 및 예외 처리 확인됨

복호화 로직이 잘 구현되어 있습니다:

  • 입력 검증 (Line 67-68, 75-77)
  • IV와 암호문 분리 (Line 79-84)
  • GCM 인증 태그 검증 실패 시 SecurityException 발생 (Line 95-98)
  • 적절한 예외 계층 구조

이는 데이터 변조 시도를 감지하고 적절히 처리할 수 있게 합니다.

backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java (5)

72-98: 회원가입 엔드포인트 구현 확인됨

입력 검증(@Valid), 적절한 HTTP 상태 코드(201 Created), OpenAPI 문서화가 잘 되어 있습니다.


122-140: 일반 로그인 엔드포인트 구현 확인됨

Access Token과 Refresh Token 발급, HttpOnly 쿠키 설정이 적절하게 구현되어 있습니다. 다만 Line 129에서 secure(false)로 설정되어 있는데, 이는 개발 환경용으로 보입니다.

프로덕션 배포 시 secure(true)로 변경되는지 확인해 주세요.


340-381: 토큰 재발급 엔드포인트 구현 확인됨

Refresh token을 검증하고 새로운 access token을 발급하는 로직이 잘 구현되어 있습니다. Refresh token이 임박하면 새로 발급하여 쿠키를 갱신하는 로직도 포함되어 있습니다.

다만 RefreshTokenServiceImpl의 Line 45에 토큰 검증 버그가 있으므로, 해당 버그가 수정되어야 이 엔드포인트가 정상적으로 작동합니다.


409-445: 로그아웃 엔드포인트 구현 확인됨

토큰 무효화 및 refresh 쿠키 삭제가 적절하게 구현되어 있습니다. 멱등성 보장을 위해 만료된 토큰에 대해서도 200 OK를 반환하는 것은 좋은 설계입니다.


110-111: 정적 분석 도구의 오탐 관련

Gitleaks가 Line 110, 111, 217, 324에서 API 키를 탐지했다고 보고하고 있으나, 이는 OpenAPI 문서의 예시 JWT 토큰 문자열입니다. 실제 secrets가 아니므로 보안 문제가 아닙니다.

Also applies to: 217-217, 324-324

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~

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.

2 participants