-
Notifications
You must be signed in to change notification settings - Fork 0
Enhance social login security and metadata validation with Redis TTL #143
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
Conversation
- Removed outdated Redis script executors and consolidated token management logic. - Introduced a new RedisScriptExecutor interface for better abstraction and handling of Redis scripts. - Enhanced error handling in token management, replacing BaseException with AuthException for specific authentication errors. - Updated AuthService to streamline login and sign-up processes, improving member validation and token creation. - Refactored OAuthLoginRequest to simplify OAuth handling and removed unnecessary classes for cleaner codebase. - Improved member repository methods for better retrieval and error handling. This refactor aims to enhance maintainability, security, and clarity in the authentication flow.
|
""" Walkthrough이 변경은 소셜 로그인 절차의 보안 취약점을 해결하고, 소셜 인증 메타데이터를 Redis에 30분 TTL로 저장하여 검증을 강화했어. 로그인과 회원가입 로직이 명확히 분리되고, 데이터베이스 무결성은 예외 처리로 위임됐지. 불필요한 DTO와 인터페이스가 제거되고, 인증 전용 예외와 에러 코드가 추가됐어. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant AuthService
participant SocialLinkService
participant MemberRepository
participant Redis
Client->>AuthService: 소셜 로그인 요청 (OAuthLoginRequest)
AuthService->>SocialLinkService: SocialLink 저장 (save)
SocialLinkService->>Redis: SocialLink (TTL 30분) 저장
AuthService->>MemberRepository: 이메일로 회원 조회
alt 회원 없음
AuthService-->>Client: 회원가입 안내
else 회원 존재
AuthService->>MemberRepository: validateLoginMember 호출
alt 검증 실패
AuthService-->>Client: 에러 반환
else 검증 성공
AuthService-->>Client: 로그인 성공, 토큰 발급
end
end
Client->>AuthService: 회원가입 요청 (SignUpMemberRequest)
AuthService->>SocialLinkService: SocialLink 검증 (verify)
SocialLinkService->>Redis: SocialLink 조회 및 검증
alt 검증 실패
AuthService-->>Client: 에러 반환
else 검증 성공
AuthService->>MemberRepository: 회원 저장 시도
alt 제약조건 위반
AuthService-->>Client: 중복 에러 반환
else 저장 성공
AuthService-->>Client: 회원가입 완료
end
end
Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 6
🔭 Outside diff range comments (2)
src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.java (2)
15-28: 🛠️ Refactor suggestion예외 처리 체인 탐색이 똑똑하네, 하지만 개선할 점이 있어!
cause chain을 따라가면서 RedisCommandExecutionException을 찾는 로직은 좋은데, 무한 루프 방지가 없네, fool!
무한 루프 방지를 추가해:
default void handleRedisException(RedisSystemException e) { // Check if the cause is a RedisCommandExecutionException Throwable cause = e.getCause(); + int depth = 0; + final int MAX_DEPTH = 10; - while (cause != null) { + while (cause != null && depth < MAX_DEPTH) { if (cause instanceof RedisCommandExecutionException) { handleRedisScriptError(cause.getMessage()); return; } cause = cause.getCause(); + depth++; }
30-33:⚠️ Potential issue쿠키 삭제하고 예외 던지는 건 좋은데, 보안 측면에서 문제가 있어!
에러 메시지를 그대로 노출하면 내부 정보가 leak될 수 있어, fool! Redis 스크립트 에러는 민감한 정보를 포함할 수 있거든.
에러 메시지를 일반화하는 게 어때:
default void handleRedisScriptError(String errorMessage) { HttpResponseUtil.addCookie(AuthConstants.REFRESH_TOKEN_HEADER_NAME, "", 0); - throw new BaseException(errorMessage, ErrorCode.INTERNAL_SERVER_ERROR); + // 보안을 위해 일반적인 에러 메시지 사용 + throw new BaseException("Redis operation failed", ErrorCode.INTERNAL_SERVER_ERROR); }🧰 Tools
🪛 ast-grep (0.38.1)
[warning] 30-30: The application does not appear to verify inbound requests which can lead to a Cross-site request forgery (CSRF) vulnerability. If the application uses cookie-based authentication, an attacker can trick users into sending authenticated HTTP requests without their knowledge from any arbitrary domain they visit. To prevent this vulnerability start by identifying if the framework or library leveraged has built-in features or offers plugins for CSRF protection. CSRF tokens should be unique and securely random. The
Synchronizer TokenorDouble Submit Cookiepatterns with defense-in-depth mechanisms such as thesameSitecookie flag can help prevent CSRF. For more information, see: [Cross-site request forgery prevention](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Req\ uest_Forgery_Prevention_Cheat_Sheet.html).
Context: HttpResponseUtil.addCookie(AuthConstants.REFRESH_TOKEN_HEADER_NAME, "", 0);
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application(cookie-missing-samesite-java)
🧹 Nitpick comments (4)
src/main/java/com/juu/juulabel/redis/ScriptRegistry.java (1)
15-18: 스트림 API 사용이 깔끔하네, 하지만 조심해야 할 게 있어!executors 리스트에 중복된 클래스 이름이 있으면 마지막 것만 남고 나머지는 덮어씌워져, fool!
중복 체크를 추가하는 게 어떨까:
public ScriptRegistry(List<RedisScriptExecutor<?, ?>> executors) { - this.scripts = executors.stream() - .collect(Collectors.toMap(e -> e.getClass().getSimpleName(), Function.identity())); + this.scripts = executors.stream() + .collect(Collectors.toMap( + e -> e.getClass().getSimpleName(), + Function.identity(), + (existing, replacement) -> { + throw new IllegalStateException( + "Duplicate executor class name: " + existing.getClass().getSimpleName()); + })); }src/main/java/com/juu/juulabel/common/factory/OAuthProviderFactory.java (1)
32-40: 코드 가독성을 개선해야 해, fool!인라인으로 액세스 토큰을 가져오는 부분이 읽기 어려워. 변수로 분리하면 더 명확해질 거야!
public OAuthUser getOAuthUser(OAuthLoginRequest oAuthLoginRequest) { Provider provider = oAuthLoginRequest.provider(); - String accessToken = getOAuthToken( - provider, - oAuthLoginRequest.redirectUri(), - oAuthLoginRequest.code()).accessToken(); + OAuthToken token = getOAuthToken( + provider, + oAuthLoginRequest.redirectUri(), + oAuthLoginRequest.code()); + String accessToken = token.accessToken(); return getOAuthProvider(provider).getOAuthUser(accessToken); }src/main/java/com/juu/juulabel/member/repository/MemberWriter.java (2)
76-91: 에러 메시지 파싱 로직 개선이 필요합니다I pity the fool who relies on error message strings! 현재 구현은 동작하지만, 데이터베이스별로 에러 메시지 형식이 다를 수 있습니다:
- MySQL과 PostgreSQL의 에러 메시지 형식이 다름
- 데이터베이스 버전 업그레이드 시 메시지가 변경될 수 있음
- 제약조건 이름이 변경되면 코드도 수정해야 함
더 안정적인 방법을 고려해보세요:
private boolean isNicknameConstraintViolation(String errorMessage) { String lowerCaseMessage = errorMessage.toLowerCase(); + // Consider using Spring's DataAccessException hierarchy + // or database-specific error codes instead of message parsing return lowerCaseMessage.contains("unique_nickname") || (lowerCaseMessage.contains("duplicate entry") && lowerCaseMessage.contains("unique_nickname")); }장기적으로는 Spring의
DuplicateKeyException이나 데이터베이스별 에러 코드를 활용하는 것이 더 안정적일 수 있습니다.
58-60: 알 수 없는 제약조건 위반 시 로깅 추가를 권장합니다I pity the fool who doesn't log important errors! 인식하지 못한 제약조건 위반이 발생했을 때 디버깅을 위해 로그를 남기는 것이 좋습니다.
// Re-throw the original exception if it's not a recognized constraint violation +log.warn("Unhandled constraint violation: {}", errorMessage, e); throw new InvalidParamException(ErrorCode.INTERNAL_SERVER_ERROR);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (33)
docs/pr/PR-141-refactor---auth.md(1 hunks)src/main/java/com/juu/juulabel/auth/domain/SocialLink.java(1 hunks)src/main/java/com/juu/juulabel/auth/executor/LoginRefreshTokenScriptExecutor.java(1 hunks)src/main/java/com/juu/juulabel/auth/executor/RevokeRefreshTokenByIndexKeyExecutor.java(1 hunks)src/main/java/com/juu/juulabel/auth/executor/RotateRefreshTokenScriptExecutor.java(2 hunks)src/main/java/com/juu/juulabel/auth/executor/SaveRefreshTokenScriptExecutor.java(1 hunks)src/main/java/com/juu/juulabel/auth/repository/RefreshTokenRepositoryImpl.java(1 hunks)src/main/java/com/juu/juulabel/auth/repository/SocialLinkRepository.java(1 hunks)src/main/java/com/juu/juulabel/auth/service/AuthService.java(2 hunks)src/main/java/com/juu/juulabel/auth/service/FraudDetectionService.java(0 hunks)src/main/java/com/juu/juulabel/auth/service/RiskAssessment.java(0 hunks)src/main/java/com/juu/juulabel/auth/service/SocialLinkService.java(1 hunks)src/main/java/com/juu/juulabel/auth/service/TokenService.java(3 hunks)src/main/java/com/juu/juulabel/common/constants/AuthConstants.java(1 hunks)src/main/java/com/juu/juulabel/common/dto/request/OAuthLoginRequest.java(1 hunks)src/main/java/com/juu/juulabel/common/exception/AuthException.java(1 hunks)src/main/java/com/juu/juulabel/common/exception/code/ErrorCode.java(1 hunks)src/main/java/com/juu/juulabel/common/exception/handler/GlobalExceptionHandler.java(3 hunks)src/main/java/com/juu/juulabel/common/factory/OAuthProviderFactory.java(1 hunks)src/main/java/com/juu/juulabel/common/provider/JwtTokenProvider.java(0 hunks)src/main/java/com/juu/juulabel/common/util/HashingUtil.java(1 hunks)src/main/java/com/juu/juulabel/member/domain/Member.java(5 hunks)src/main/java/com/juu/juulabel/member/repository/MemberReader.java(2 hunks)src/main/java/com/juu/juulabel/member/repository/MemberWriter.java(1 hunks)src/main/java/com/juu/juulabel/member/repository/WithdrawalRecordReader.java(0 hunks)src/main/java/com/juu/juulabel/member/request/OAuthLoginInfo.java(0 hunks)src/main/java/com/juu/juulabel/member/util/MemberUtils.java(1 hunks)src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.java(1 hunks)src/main/java/com/juu/juulabel/redis/RedisScriptName.java(1 hunks)src/main/java/com/juu/juulabel/redis/ScriptRegistry.java(1 hunks)src/main/resources/scripts/login_refresh_token.lua(1 hunks)src/main/resources/scripts/rotate_refresh_token.lua(2 hunks)src/main/resources/scripts/save_refresh_token.lua(1 hunks)
💤 Files with no reviewable changes (5)
- src/main/java/com/juu/juulabel/member/request/OAuthLoginInfo.java
- src/main/java/com/juu/juulabel/auth/service/FraudDetectionService.java
- src/main/java/com/juu/juulabel/common/provider/JwtTokenProvider.java
- src/main/java/com/juu/juulabel/member/repository/WithdrawalRecordReader.java
- src/main/java/com/juu/juulabel/auth/service/RiskAssessment.java
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/main/java/com/juu/juulabel/auth/service/TokenService.java (1)
src/main/java/com/juu/juulabel/common/util/HashingUtil.java (1)
HashingUtil(8-23)
src/main/java/com/juu/juulabel/auth/executor/RotateRefreshTokenScriptExecutor.java (1)
src/main/java/com/juu/juulabel/common/util/HttpResponseUtil.java (1)
HttpResponseUtil(8-46)
src/main/java/com/juu/juulabel/auth/domain/SocialLink.java (3)
src/main/java/com/juu/juulabel/common/exception/AuthException.java (1)
Getter(6-24)src/main/java/com/juu/juulabel/member/domain/Member.java (1)
Getter(15-137)src/main/java/com/juu/juulabel/common/constants/AuthConstants.java (1)
NoArgsConstructor(8-23)
🔇 Additional comments (38)
src/main/java/com/juu/juulabel/common/util/HashingUtil.java (1)
8-12: 아, 이 바보 같은 유틸리티 클래스 패턴 제대로 구현했네!final 클래스에 private 생성자로 인스턴스화를 막는 건 정말 좋은 패턴이야, fool! AssertionError를 던지는 것도 누군가 리플렉션으로 생성자 호출하는 걸 막아주지.
src/main/java/com/juu/juulabel/auth/executor/SaveRefreshTokenScriptExecutor.java (1)
16-16: 패키지 리팩토링 깔끔하게 했네, fool!Redis 관련 클래스들을 별도 패키지로 분리하는 건 관심사 분리 관점에서 좋은 결정이야.
src/main/java/com/juu/juulabel/redis/ScriptRegistry.java (1)
1-1: Redis 패키지로 이동한 게 딱 맞네, fool!스크립트 레지스트리가 redis 패키지에 있는 게 훨씬 논리적이야.
src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.java (1)
1-1: Redis 패키지로 이동한 게 완벽해, fool!Redis 스크립트 실행 관련 인터페이스가 redis 패키지에 있는 게 맞지.
src/main/java/com/juu/juulabel/auth/executor/LoginRefreshTokenScriptExecutor.java (1)
15-15: 수고했어요, fool! 이 import 추가는 완벽해!Redis 관련 인터페이스를 전용 패키지로 이동시키는 리팩터링의 일환으로 보이는데, 이미 구현하고 있던 인터페이스에 대한 import를 추가한 것이니 아무 문제 없어요. I pity the fool who doesn't organize their packages properly!
src/main/resources/scripts/rotate_refresh_token.lua (2)
1-3: 명명 규칙 표준화가 훌륭해요, fool!Redis 키 명명 규칙을 PascalCase에서 lowercase with underscores로 표준화한 것이 좋습니다. I pity the fool who doesn't keep their naming conventions consistent! 이런 일관성은 유지보수성을 크게 향상시켜요.
23-23: 패턴 업데이트도 완벽해요!revokeAllMemberTokens 함수의 스캔 패턴도 새로운 명명 규칙에 맞게 업데이트했군요. 코드 전반에 걸친 일관성을 보장하는 좋은 변경사항이에요. I pity the fool who forgets to update patterns when changing naming conventions!
src/main/java/com/juu/juulabel/auth/service/TokenService.java (2)
9-9: HashingUtil import 추가가 현명한 선택이에요!토큰 해싱 로직을 전용 유틸리티 클래스로 중앙화하는 것은 관심사 분리에 훌륭한 접근이에요. I pity the fool who doesn't centralize their utility functions!
72-72: 해싱 메서드 교체가 완벽해요, fool!
jwtTokenProvider.hashToken()에서HashingUtil.hashSha256()로 변경한 것이 좋습니다. 관련 코드 스니펫을 보니 HashingUtil 구현이 견고해요:
- SHA-256 알고리즘 사용
- URL-safe Base64 인코딩
- 적절한 예외 처리
- 인스턴스화 불가능한 유틸리티 클래스 설계
I pity the fool who doesn't use proper utility classes for common operations!
Also applies to: 109-109
src/main/java/com/juu/juulabel/auth/repository/RefreshTokenRepositoryImpl.java (2)
6-7: Redis 패키지 재구성이 훌륭해요!Redis 스크립트 관련 클래스들을 전용 redis 패키지로 이동시킨 것이 좋은 조직화예요. I pity the fool who doesn't organize their packages properly! 이런 구조화는 코드 관리와 유지보수성을 크게 향상시켜요.
14-14: 클래스명 변경이 더 명확해요, fool!
RedisRefreshTokenRepository에서RefreshTokenRepositoryImpl로 변경한 것이 더 명확한 명명이에요. Implementation 클래스임을 명시적으로 나타내는 좋은 네이밍 컨벤션이죠. I pity the fool who doesn't follow proper naming conventions!src/main/resources/scripts/login_refresh_token.lua (1)
1-2: 변경된 Redis 키 네이밍 컨벤션 반영
바보 같은 혼용은 용서 못해! Lua 스크립트 주석에서 PascalCase 대신 snake_case(refresh_token,refresh_index)로 예시 키 네이밍을 일관되게 업데이트했으니 확인해봐.src/main/resources/scripts/save_refresh_token.lua (1)
1-2: 키 네이밍 예시를 snake_case로 통일
파스칼 케이스는 이제 그만! 설명 주석에서refresh_token,refresh_index로 통일했으니 바보 같은 혼용은 안 하길 바란다.src/main/java/com/juu/juulabel/auth/executor/RevokeRefreshTokenByIndexKeyExecutor.java (1)
14-14:Details
✅ Verification successful
RedisScriptExecutor 인터페이스 임포트 적용
추상화 레벨을 올리기 위해 새로com.juu.juulabel.redis.RedisScriptExecutor를 임포트했네. 이 변경 안 하면 진짜 바보일 걸? 경로와handleRedisException제공 여부를 확인해봐.
🏁 Script executed:
#!/bin/bash # RedisScriptExecutor 인터페이스 위치 및 handleRedisException 정의 확인 rg -n "interface RedisScriptExecutor" -R src/main/java rg -n "handleRedisException" -R src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.javaLength of output: 960
Hey, 바보 취급하고 싶지 않으면 이걸로 다시 확인해! 난 바보를 불쌍히 여기는 놈이거든!
🏁 Script executed:
#!/bin/bash # RedisScriptExecutor 인터페이스 정의와 handleRedisException 구현 여부 재확인 rg -n 'interface RedisScriptExecutor' src/main/java rg -n 'handleRedisException' src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.javaLength of output: 321
확인 완료: RedisScriptExecutor 인터페이스와 handleRedisException 구현이 존재합니다
인터페이스와 기본 예외 처리 메서드가 아래 위치에서 정상적으로 확인되었으니 변경 사항은 그대로 유지해도 좋습니다. 난 바보를 불쌍히 여기지만, 이번엔 문제가 없으니 안심해도 돼!
• src/main/java/com/juu/juulabel/redis/RedisScriptExecutor.java
– Line 12: public interface RedisScriptExecutor<T, R>
– Line 15: default void handleRedisException(RedisSystemException e) { … }src/main/java/com/juu/juulabel/auth/repository/SocialLinkRepository.java (1)
1-9: 새로운 SocialLinkRepository 인터페이스 추가
소셜 링크 도메인 CRUD를 위한CrudRepository<SocialLink, String>확장이네. 이걸 무시하는 바보를 난 불쌍히 여긴다! 필요 시 커스텀 메서드를 추가하는 것도 고려해봐.src/main/java/com/juu/juulabel/redis/RedisScriptName.java (1)
5-11: RedisScriptName enum 주석 및 상수 업데이트
리프레시 토큰 스크립트 구분을 위해// Refresh Token주석을 넣고,REVOKE_REFRESH_TOKEN_BY_INDEX_KEY뒤에 쉼표를 일관성 있게 추가했어. 이런 기본도 못 챙기는 바보는 안 되지!src/main/java/com/juu/juulabel/auth/executor/RotateRefreshTokenScriptExecutor.java (2)
18-18: 새로운 예외 처리와 패키지 재구성이 올바르게 적용되었어, fool!AuthException 임포트와 Redis 패키지 재구성이 적절히 반영되었네. I pity the fool who doesn't organize their packages properly!
Also applies to: 21-21
57-61: 인증 관련 예외 처리가 훨씬 명확해졌어, fool!BaseException 대신 AuthException을 사용하는 것이 인증 도메인의 책임을 명확히 하고 에러 추적을 개선하네. I pity the fool who uses generic exceptions for specific authentication errors! 각 에러 코드(REFRESH_TOKEN_NOT_FOUND, REFRESH_TOKEN_REUSE_DETECTED, DEVICE_ID_MISMATCH)가 적절하게 매핑되어 있어.
src/main/java/com/juu/juulabel/member/repository/MemberReader.java (2)
13-13: Optional 임포트가 필요하다, fool!새로운 getOptionalByEmail 메서드를 위한 적절한 임포트네. I pity the fool who forgets their imports!
32-34: 유연한 회원 조회 메서드 추가가 훌륭해, fool!getOptionalByEmail 메서드는 예외를 던지지 않는 대안을 제공해서 인증 서비스에서 더 유연한 제어 흐름을 가능하게 하네. I pity the fool who always throws exceptions when Optional can handle absence gracefully! 기존 getByEmail 메서드와 함께 두 가지 사용 패턴을 모두 지원할 수 있어.
src/main/java/com/juu/juulabel/common/dto/request/OAuthLoginRequest.java (1)
8-11: OAuth 요청 레코드 단순화가 완벽해, fool!toDto() 메서드를 제거하고 불필요한 데이터 변환 계층을 없앤 것이 현명하네. I pity the fool who keeps unnecessary mapping methods! 유효성 검증 어노테이션과 한국어 오류 메시지가 적절히 유지되고 있어. 레코드가 더 간결하고 유지보수하기 쉬워졌다.
src/main/java/com/juu/juulabel/member/util/MemberUtils.java (1)
52-62: 회원 데이터 처리 중앙화가 좋은 접근이야, fool!null 체크와 함께 alcohol types와 terms agreements 처리를 하나의 메서드로 통합한 것이 AuthService의 복잡성을 줄여주네. I pity the fool who scatters member processing logic everywhere!
src/main/java/com/juu/juulabel/member/domain/Member.java (2)
34-34: I pity the fool who doesn't enforce database constraints! 좋은 개선이야!닉네임 필드에
unique = true제약 조건을 추가한 건 데이터베이스 레벨에서 무결성을 보장하는 올바른 접근이야. 이제 애플리케이션 레벨의 중복 검사 대신 데이터베이스가 직접 처리하게 되어 더 안전하고 효율적이지.
78-88: I pity the fool who doesn't like clean code! 멤버 생성 로직이 깔끔해졌어!빌더 패턴을 사용한 멤버 생성 로직이 간결하고 명확해졌어. 기본값들이 명시적으로 설정되어 있어서 의도가 명확하게 드러나지.
src/main/java/com/juu/juulabel/common/exception/AuthException.java (1)
1-24: I pity the fool who doesn't separate authentication concerns! 인증 전용 예외 클래스 설계가 훌륭해!
AuthException클래스 설계가 매우 잘 되어 있어:
BaseException을 확장해서 일관성 유지- 다양한 생성자로 유연성 제공
- 기본 에러 코드를
HIGH_SECURITY_RISK로 설정한 것이 보안상 안전한 접근- 인증 관련 예외와 일반 비즈니스 예외의 명확한 분리
이런 설계는 인증/인가 오류를 더 정확하게 분류하고 처리할 수 있게 해줘.
src/main/java/com/juu/juulabel/auth/service/SocialLinkService.java (2)
23-33: I pity the fool who doesn't hash sensitive data! 소셜 링크 저장 로직이 보안을 잘 고려했어!
save메서드의 구현이 보안 측면에서 훌륭해:
- 이메일을 SHA-256으로 해싱해서 개인정보 보호
- 디바이스 ID, User Agent, IP 주소 등 클라이언트 메타데이터 수집으로 추가 검증 가능
- Redis TTL을 통한 임시 저장으로 보안 위험 최소화
이런 접근은 소셜 로그인 우회 공격을 효과적으로 방지할 수 있어.
35-46: I pity the fool who doesn't validate client metadata! 검증 로직이 철저해!
verify메서드의 보안 검증이 매우 포괄적이야:
- 해시된 이메일로 소셜 링크 조회
- 소셜 링크가 없으면
AuthException발생- 프로바이더, 프로바이더 ID, 클라이언트 메타데이터 모두 검증
- 사용 후
markAsUsed()호출로 재사용 방지이런 다층 검증은 소셜 로그인 보안을 크게 강화시켜.
src/main/java/com/juu/juulabel/common/exception/handler/GlobalExceptionHandler.java (3)
35-38: I pity the fool who doesn't handle parameter validation! InvalidParamException 핸들러가 깔끔해!
InvalidParamException핸들러가 간단하고 효율적이야. 별도의 로깅 없이 에러 코드만 반환하는 것이 적절해 - 파라미터 검증 오류는 일반적으로 클라이언트 측 문제이니까.
40-45: I pity the fool who doesn't take authentication seriously! AuthException 핸들러가 완벽해!
AuthException핸들러의 구현이 보안 관점에서 훌륭해:
- 로깅으로 보안 이벤트 추적 가능
- Sentry 리포팅으로 모니터링 강화
- 에러 코드와 메시지를 모두 포함한 상세 응답
- 인증 관련 오류의 특별한 처리 필요성을 잘 반영
이런 접근은 보안 사고 대응과 디버깅에 큰 도움이 될 거야.
47-51: I pity the fool who doesn't reduce log noise! 로깅 제거가 현명한 판단이야!
BaseException핸들러에서 로깅을 제거한 건 좋은 개선이야. Sentry 리포팅은 유지하면서 로그 노이즈는 줄여서, 모니터링은 계속 되지만 로그는 더 깔끔해졌어.src/main/java/com/juu/juulabel/auth/domain/SocialLink.java (1)
1-110: 전반적인 구현은 훌륭해, 잘했어!소셜 링크 보안 기능이 잘 구현됐어:
- Redis TTL로 30분 만료 시간 설정 ✓
- 일회용 검증 로직 ✓
- 빌더 패턴 사용 ✓
Mr. T가 인정하는 solid한 코드야!
src/main/java/com/juu/juulabel/common/factory/OAuthProviderFactory.java (1)
24-30: Switch expression 사용이 멋져!Java의 현대적인 기능을 잘 활용했어. Default 케이스로 예외 처리도 완벽해!
src/main/java/com/juu/juulabel/auth/service/AuthService.java (1)
36-60: 보안 강화가 훌륭해!소셜 링크 검증으로 인증 우회를 막는 게 정말 똑똑한 방법이야:
- 신규 회원은 소셜 링크 저장 ✓
- 기존 회원은 provider 검증 ✓
- 명확한 관심사 분리 ✓
Mr. T가 자랑스러워해!
src/main/java/com/juu/juulabel/common/exception/code/ErrorCode.java (2)
35-36: 이름 변경이 적절합니다!I pity the fool who confuses Authentication with Authorization! "Authorization"으로 변경한 것이 정확합니다. 이 섹션의 에러들은 권한 확인과 관련되어 있으니까요.
40-52: 새로운 AuthException 에러 코드들이 잘 정의되었습니다!I pity the fool who doesn't separate authentication errors properly! 보안 관련 에러들을 따로 분리한 건 훌륭한 결정입니다:
- 인증 실패는 401 (UNAUTHORIZED)
- 권한/보안 위반은 403 (FORBIDDEN)
- 일반적인 요청 오류는 400 (BAD_REQUEST)
HTTP 상태 코드 매핑이 RESTful 원칙에 잘 부합합니다.
docs/pr/PR-141-refactor---auth.md (2)
16-39: 보안 취약점 해결 방안이 훌륭합니다!I pity the fool who doesn't secure their social login! Redis TTL 기반 검증은 정말 똑똑한 해결책입니다. 30분 TTL로 소셜 로그인 메타데이터를 저장하고 회원가입 시 검증하는 방식이 보안을 크게 강화합니다.
66-88: 데이터베이스 최적화가 인상적입니다!I pity the fool who makes unnecessary database queries! 쿼리를 4개에서 1개로 줄인 건 정말 멋진 최적화입니다. 낙관적 접근법(Optimistic approach)을 사용해서 대부분의 경우 단일 INSERT로 처리하고, 충돌이 발생할 때만 예외를 처리하는 방식이 효율적입니다.
단, 데이터베이스에 적절한 유니크 제약조건이 설정되어 있는지 확인이 필요합니다!
src/main/java/com/juu/juulabel/member/repository/MemberWriter.java (1)
28-34: 예외 처리 로직이 잘 구현되었습니다!I pity the fool who doesn't handle database exceptions properly! 데이터베이스 제약조건 위반을 비즈니스 예외로 변환하는 방식이 깔끔합니다.
src/main/java/com/juu/juulabel/common/constants/AuthConstants.java
Outdated
Show resolved
Hide resolved
- Simplified error messages in SocialLink to enhance security and clarity. - Updated AuthService to use orElseGet for better performance and readability. - Corrected a typo in AuthConstants for the refresh token prefix. - Enhanced IpAddressExtractor to utilize orElseGet for improved null handling. - Refactored FollowReader to use orElseGet for consistency in null returns. These changes aim to improve maintainability and clarity across the authentication and follow management components.
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: 0
🧹 Nitpick comments (1)
src/main/java/com/juu/juulabel/follow/repository/FollowReader.java (1)
24-26: I pity the fool who uses.orElseGet()when.orElse()is better, fool!야, 이 변경사항이 불필요해!
null같은 리터럴 값에는.orElse(null)이.orElseGet(() -> null)보다 더 적절해..orElseGet()은 비싼 연산이나 부작용이 있는 메서드 호출에만 사용해야 해. 여기서는 불필요한 람다 오버헤드만 추가하고 있어, fool!다음 diff로 되돌리는 걸 권장해:
- .orElseGet(() -> null); + .orElse(null);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/main/java/com/juu/juulabel/auth/domain/SocialLink.java(1 hunks)src/main/java/com/juu/juulabel/auth/service/AuthService.java(2 hunks)src/main/java/com/juu/juulabel/common/constants/AuthConstants.java(1 hunks)src/main/java/com/juu/juulabel/common/exception/code/ErrorCode.java(1 hunks)src/main/java/com/juu/juulabel/common/util/IpAddressExtractor.java(1 hunks)src/main/java/com/juu/juulabel/follow/repository/FollowReader.java(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/main/java/com/juu/juulabel/common/util/IpAddressExtractor.java
🚧 Files skipped from review as they are similar to previous changes (3)
- src/main/java/com/juu/juulabel/common/constants/AuthConstants.java
- src/main/java/com/juu/juulabel/auth/service/AuthService.java
- src/main/java/com/juu/juulabel/auth/domain/SocialLink.java
🔇 Additional comments (4)
src/main/java/com/juu/juulabel/common/exception/code/ErrorCode.java (4)
35-39: 인증과 권한 구분이 명확해졌어, 이걸 안 하는 fool들을 불쌍하게 여기지!"Authentication"에서 "Authorization"으로 주석 변경이 의미론적으로 정확해. DEVICE_ID_REQUIRED와 OAUTH_PROVIDER_NOT_FOUND는 실제로 권한 부여(authorization) 관련 에러니까 말이야.
40-52: 새로운 AuthException 섹션이 보안을 강화했어, I pity the fool who tries to hack this!소셜 로그인 보안 검증을 위한 새로운 에러 코드들이 추가됐는데, 이건 정말 좋은 보안 강화야:
SOCIAL_LINK_NOT_FOUND,SOCIAL_LINK_ALREADY_USED: 소셜 링크 생명주기 관리PROVIDER_ID_MISMATCH,USER_AGENT_MISMATCH: 세션 하이재킹 방지HIGH_SECURITY_RISK: 포괄적 보안 위협 감지HTTP 상태 코드 매핑도 적절해.
FORBIDDEN (403)은 보안 위반에 맞고,BAD_REQUEST (400)는 클라이언트 요청 오류에 적합하지.
44-44: 토큰 재사용 감지의 HTTP 상태 코드 변경이 보안 관점에서 더 정확해, fool!
REFRESH_TOKEN_REUSE_DETECTED를FORBIDDEN (403)에서UNAUTHORIZED (401)로 변경한 건 정확한 판단이야. 토큰 재사용은 인증 자체의 문제지, 권한 부족 문제가 아니거든. 이렇게 하면 클라이언트가 토큰을 다시 얻어야 한다는 걸 명확히 알 수 있어.
49-49: DEVICE_ID_MISMATCH도 보안 강화를 위해 올바른 상태 코드로 변경됐어!AI 요약에 따르면
DEVICE_ID_MISMATCH가BAD_REQUEST (400)에서FORBIDDEN (403)으로 변경됐다고 하는데, 이건 보안 위반에 대한 더 적절한 응답이야. 디바이스 ID 불일치는 잠재적 보안 위협이니까 403이 맞아.
This refactor aims to enhance maintainability, security, and clarity in the authentication flow.
Summary by CodeRabbit
신규 기능
버그 수정
리팩터링
기타