-
Notifications
You must be signed in to change notification settings - Fork 2
[BE] Sisc1 200 [FEAT] 회원 탈퇴 및 refreshtoken 구현, Swagger 수정 #77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "SISC1-200-BE-\uD68C\uC6D0-\uD0C8\uD1F4-\uBC0F-refreshtoken-\uAD6C\uD604"
Conversation
Walkthrough회원가입·일반 로그인·다중 제공자 OAuth 흐름을 처리하는 신규 Changes
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
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 갱신 가능)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 주의 깊게 검토할 영역:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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, "엑세스 토큰이 만료되었습니다. 재발급이 필요합니다."), - // USERbackend/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
📒 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
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
backend/src/test/java/org/sejongisc/backend/backtest/controller/BacktestControllerTest.java
Show resolved
Hide resolved
backend/src/test/java/org/sejongisc/backend/backtest/service/BacktestServiceTest.java
Show resolved
Hide resolved
There was a problem hiding this 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
📒 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 추가
LocalDateTimeimport가 새로운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의 표준 패턴을 잘 따르고 있으며, 필요한 의존성들이 적절히 주입되고 있습니다.
backend/src/main/java/org/sejongisc/backend/auth/controller/AuthController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/UserOauthAccount.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
Outdated
Show resolved
Hide resolved
# Conflicts: # backend/src/main/resources/application.yml
There was a problem hiding this 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 생성과 암호문 결합 로직도 올바르게 구현되었습니다.
개선 권장사항:
입력 검증 추가:
token파라미터에 대한 null/빈 문자열 검증이 없어 NPE 발생 시 원인 파악이 어려울 수 있습니다.예외 처리 구체화: 모든 예외를
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
📒 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!
deleteByUserId와saveOrUpdateToken메서드 모두 트랜잭션 처리가 적절하며,saveOrUpdateToken의ifPresentOrElse패턴을 사용한 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
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/service/OauthUnlinkServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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 인증 태그 실패를 별도로 처리하는 예외 핸들링(
AEADBadTagException→SecurityException), 그리고 일관된 한국어 오류 메시지가 모두 우수합니다.선택사항: 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인자 형태로 구성되어 있습니다.
다만 다음 개선사항을 고려해주세요:
- 예외를 throw하는 상황에서는
log.error를 사용하고 스택 트레이스도 로깅- (선택사항) 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); }동일한 패턴을
unlinkGoogle과unlinkGithub메서드에도 적용할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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: 클래스 구조 및 의존성 주입이 적절합니다.서비스 구현체의 구조가 깔끔하고, 필요한 설정 값들이 적절히 주입되고 있습니다.
backend/src/main/java/org/sejongisc/backend/common/auth/jwt/TokenEncryptor.java
Outdated
Show resolved
Hide resolved
discipline24
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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: 암호화 키 검증 및 기본값 개선 필요두 가지 문제가 있습니다:
키 길이 검증 오류: Line 25에서
key.length() != 16은 문자 개수를 확인하지만, UTF-8 인코딩 후 실제 바이트 길이는 다를 수 있습니다. 멀티바이트 문자(예: 한글)가 포함되면 16문자여도 16바이트가 아닐 수 있습니다.안전하지 않은 기본값: 기본값
"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
📒 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
backend/src/main/java/org/sejongisc/backend/auth/service/RefreshTokenServiceImpl.java
Outdated
Show resolved
Hide resolved
discipline24
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다~
Summary by CodeRabbit
새 기능
개선 사항
문서
테스트
기타