Skip to content

Conversation

@minjee2758
Copy link
Collaborator

@minjee2758 minjee2758 commented Jun 26, 2025

작업 내용

  • OAuth 서비스 코드 가독성 향상을 위한 메서드 분리
  • 깃허브 이메일 Private 대응 로직 추가
  • 깃허브 AccessToken 암호화 저장 기능 구현
  • 통합 OAuth 컨트롤러(확장성 고려)

변경 사항

  • CustomOAuth2UserService의 3중 if문을 processOAuth2User() 메서드로 추출

  • 깃허브 이메일 처리 (GitHub 이메일이 null일 경우 처리)

String email = attributes.get("email");
if (email == null) {
    Long githubId = (Long) attributes.get("id");
    email = githubId + "@github.com"; // 고유 ID 기반 이메일 생성
}
  • 소셜 로그인 추가 시 AI리뷰 토큰 부여

    • 기존 회원이 새로운 소셜 로그인 추가 시 자동으로 발급 (기존에 인증 안했던 회원에 한해서, 최초 인증일때만)
  • 통합 OAuth 컨트롤러

    • /api/oauth2/authorize/{provider} 로 provider RequestParam 값에 따라 이동
  • 깃허브 AccessToken 저장

    • AESUtil 클래스 구현: AES 기반 토큰 암호화 후 User테이블에 저장

해결해야 할 문제

  • SALT 암호화까지 도입해야할지 고민
  • 깃허브 토큰 만료 시 받아올 refresh 토큰 구현 필요

참고 사항

OAuth 해보고싶으시면

  • 깃허브
    http://localhost:8080/api/oauth2/authorize/github
  • 구글
    http://localhost:8080/api/oauth2/authorize/google
    를 크롬창에 넣고 엔터 누르시면 됩니다

참고 이미지

(1) github 이메일 제공 관련
image
이렇게 풀려있어야 넘어옵니다

(2) DB에 등록되는 화면

  • 그냥 이메일로만 가입햇을때 (verified = false, github_url=null)
    image

  • github 등록하면

image 토큰 20개 지급, verified= true, github_url 자동 등, github access-token 암호화 저장

코드 리뷰 전 확인 체크리스트

  • 불필요한 콘솔 로그, 주석 제거
  • 커밋 메시지 컨벤션 준수 (type : )
  • 기능 정상 동작 확인

Summary by CodeRabbit

  • 신규 기능

    • GitHub OAuth2 로그인을 지원하는 엔드포인트 및 관련 응답 클래스가 추가되었습니다.
    • GitHub 액세스 토큰을 AES로 암호화하여 안전하게 저장하는 기능이 도입되었습니다.
    • OAuth2 로그인 시 리디렉션 URI를 통한 토큰 전달이 지원됩니다.
    • AES 암호화/복호화 유틸리티가 추가되었습니다.
  • 버그 수정

    • GitHub OAuth2 사용자 이메일이 없을 경우 대체 이메일 형식이 제공됩니다.
  • 환경설정

    • Google 및 GitHub OAuth2 환경 변수 및 설정이 개선되었습니다.
    • AES 암호화 키 환경변수 설정이 추가되었습니다.
  • 리팩토링 및 개선

    • OAuth2 사용자 처리 로직이 구조화되어 유지보수가 용이해졌습니다.
    • 사용자 엔티티에 GitHub 관련 정보 필드가 추가되었습니다.
    • 코드 내 불필요한 주석 및 필드가 정리되었습니다.
  • 문서화

    • Swagger를 통한 API 문서 예시 및 설명이 보강되었습니다.

@minjee2758 minjee2758 self-assigned this Jun 26, 2025
@minjee2758 minjee2758 added documentation Improvements or additions to documentation enhancement New feature or request labels Jun 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 26, 2025

"""

Walkthrough

이 변경사항은 GitHub OAuth2 로그인을 지원하기 위해 여러 계층에 걸쳐 기능을 추가하고, AES 암호화 유틸리티를 도입하며, OAuth2 인증 성공 시 GitHub 액세스 토큰을 암호화하여 저장하는 로직을 구현합니다. 또한, OAuth2 관련 API 엔드포인트와 설정이 추가 및 확장되었습니다.

Changes

파일/경로 그룹 변경 요약
build.gradle, src/main/resources/application.properties JAXB API 의존성 추가, OAuth2(Google/GitHub) 및 AES 키 환경변수 기반 설정 추가/수정
src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java AES 암호화/복호화 유틸리티 클래스 신규 추가
src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java GitHub OAuth2 로그인 성공 시 액세스 토큰 암호화 및 저장, 리다이렉트 URI 처리, 의존성 주입 및 로깅 개선
src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java 화이트리스트 URI 필터링 코드 제거
src/main/java/org/ezcode/codetest/common/security/util/SecurityPath.java PUBLIC_PATH에 OAuth2 관련 경로 추가 및 정렬
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/OAuth2Response.java getGithubId, getGithubUrl 메서드 인터페이스에 추가
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GithubOAuth2Response.java GitHub OAuth2 응답 DTO 신규 추가, Swagger 어노테이션 포함
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GoogleOAuth2Response.java getGithubId, getGithubUrl 메서드 구현(빈 문자열 반환)
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java githubAccessToken 필드 및 관련 setter, githubUser 팩토리 메서드 추가, 생성자/기존 메서드 확장
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java OAuth2Response 기반 User 생성 팩토리 클래스 신규 추가
src/main/java/org/ezcode/codetest/domain/user/model/entity/CustomOAuth2User.java getEmail()에서 email이 null일 때 githubId 기반 이메일 반환
src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java updateUserGithubAccessToken 메서드 선언 추가
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java updateUserGithubAccessToken 구현(userJpaRepository.save)
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java OAuth2 provider별 응답/유저 처리 로직 리팩토링 및 GitHub 지원, 예외 처리 개선
src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java updateUserGithubAccessToken 메서드 추가 및 위임 로직 구현
src/main/java/org/ezcode/codetest/domain/user/exception/code/AuthExceptionCode.java TOKEN_ENCODE_FAIL, REDIRECT_URI_NOT_FOUND 예외 코드 추가
src/main/java/org/ezcode/codetest/presentation/usermanagement/OAuth2Controller.java OAuth2 인증 시작 API 컨트롤러 신규 추가, redirect_uri 세션 저장 및 리다이렉트 구현
src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java mailService 필드 선언 제거
src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java OAuth2 로그인 람다 파라미터명 단순 변경

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OAuth2Controller
    participant SpringSecurity
    participant OAuthProvider as "GitHub/Google"
    participant CustomSuccessHandler
    participant AESUtil
    participant UserDomainService
    participant UserRepository

    Client->>OAuth2Controller: /api/oauth2/authorize/{provider}?redirect_uri=
    OAuth2Controller->>Client: Redirect /oauth2/authorization/{provider}
    Client->>SpringSecurity: /oauth2/authorization/{provider}
    SpringSecurity->>OAuthProvider: OAuth2 인증 요청
    OAuthProvider-->>SpringSecurity: 인증 응답 (access token 등)
    SpringSecurity->>CustomSuccessHandler: 인증 성공 콜백
    CustomSuccessHandler->>AESUtil: (GitHub일 경우) access token 암호화
    AESUtil-->>CustomSuccessHandler: 암호화된 토큰 반환
    CustomSuccessHandler->>UserDomainService: 암호화 토큰 User에 저장 요청
    UserDomainService->>UserRepository: updateUserGithubAccessToken
    UserRepository-->>UserDomainService: 저장 완료
    CustomSuccessHandler->>Client: JWT access/refresh 토큰 JSON 반환 (또는 redirect)
Loading

Possibly related PRs

Suggested reviewers

  • pokerbearkr
  • Kimminu7
  • chat26666

Poem

🐰
깃허브와 구글, 로그인 춤을 추네,
암호화 토큰은 AES로 꽁꽁 묶이고,
OAuth2 컨트롤러가 길을 안내하네.
새로운 유저 팩토리, 리다이렉트의 꿈,
깃허브 토큰 저장하며 토끼는 방긋!
Hop, hop, hop—코드에 봄이 왔어요!

"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between edd727c and 825de30.

📒 Files selected for processing (1)
  • src/main/resources/application.properties (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/resources/application.properties

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (7)
src/main/java/org/ezcode/codetest/domain/user/model/entity/CustomOAuth2User.java (1)

33-37: GitHub ID 기반 이메일 생성 시 검증 로직 추가 고려

null 이메일 처리 로직이 올바르게 구현되었지만, 다음 사항들을 고려해보세요:

  1. GitHub ID가 특수문자를 포함할 경우 유효하지 않은 이메일이 생성될 수 있습니다.
  2. @github.com 도메인을 사용하는 것이 실제 GitHub 이메일과 혼동을 일으킬 수 있습니다.

더 안전한 구현을 위해 다음과 같이 개선을 제안합니다:

public String getEmail() {
	if (oAuth2Response.getEmail() == null) {
-		return oAuth2Response.getGithubId()+"@github.com";
+		return oAuth2Response.getGithubId().replaceAll("[^a-zA-Z0-9]", "") + "@github.local";
	} else {
		return oAuth2Response.getEmail();
	}
}
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (1)

8-29: 팩토리 메서드 개선 제안

현재 구현은 좋지만 몇 가지 개선점이 있습니다:

  1. 지원하지 않는 provider에 대한 명시적 예외 처리
  2. 패스워드 생성 로직 중복 제거
  3. 메서드 파라미터 검증
+private static final Set<String> SUPPORTED_PROVIDERS = Set.of("github", "google");
+
+private static String generateRandomPassword() {
+    return UUID.randomUUID().toString();
+}
+
 public static User createSocialUser(
     OAuth2Response response,
     String nickname,
     String provider
 ) {
+    if (provider == null || provider.trim().isEmpty()) {
+        throw new IllegalArgumentException("Provider cannot be null or empty");
+    }
+    
+    String normalizedProvider = provider.toLowerCase();
+    if (!SUPPORTED_PROVIDERS.contains(normalizedProvider)) {
+        throw new IllegalArgumentException("Unsupported OAuth2 provider: " + provider);
+    }
+    
-    return switch (provider.toLowerCase()) {
+    return switch (normalizedProvider) {
         case "github" -> User.githubUser(
             response.getEmail(),
             response.getName(),
             nickname,
-            UUID.randomUUID().toString(),
+            generateRandomPassword(),
             response.getGithubUrl()
         );
         default -> User.socialUser(
             response.getEmail(),
             response.getName(),
             nickname,
-            UUID.randomUUID().toString()
+            generateRandomPassword()
         );
     };
 }
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GithubOAuth2Response.java (1)

29-35: 이메일 처리 로직 개선

현재 이메일 fallback 로직이 명확하지만, provider 체크가 불필요하고 null 안전성을 개선할 수 있습니다.

 @Override
 public String getEmail() {
-    if (attributes.get("email") == null && getProvider().equals("github")) {
-        return getGithubId()+"@github.com";
-    } else {
-        return (String) attributes.get("email");
-    }
+    Object email = attributes.get("email");
+    if (email == null) {
+        return getGithubId() + "@github.com";
+    }
+    return email.toString();
 }
src/main/java/org/ezcode/codetest/presentation/usermanagement/OAuth2Controller.java (1)

25-31: API 문서 예시 URL 업데이트 필요

API 문서의 예시 URL이 실제 도메인과 일치하지 않을 수 있습니다. 실제 서비스 도메인으로 업데이트하거나 예시임을 명확히 표시하는 것이 좋습니다.

 @Operation(summary = "GitHub & Google OAuth2 로그인 API",
     description = """
            프론트엔드에서 GitHub 로그인 버튼 클릭 시 이 API를 먼저 호출
            redirect_uri는 로그인 완료 후 accessToken과 refreshToken을 전달받을 프론트의 콜백 URL

-           예시: GET /api/oauth2/authorize/github?redirect_uri=https://ezcode.my/oauth/callback
+           예시: GET /api/oauth2/authorize/github?redirect_uri=https://your-domain.com/oauth/callback
            """)
src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java (1)

61-78: GitHub 액세스 토큰 만료 처리 고려사항

현재 구현은 잘 되어 있으나, PR에서 언급하신 토큰 만료 처리에 대해 다음을 제안드립니다:

  • GitHub 액세스 토큰은 만료되지 않지만, 사용자가 권한을 취소할 수 있습니다
  • 토큰 사용 시 401/403 에러 발생하면 재인증을 유도하는 로직 추가를 고려하세요
  • 토큰과 함께 저장 시간을 기록하여 주기적인 갱신을 고려할 수 있습니다
src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (1)

75-76: 필드 접근 제어자 일관성

githubAccessToken 필드를 다른 필드들과 일관성을 위해 private으로 선언하는 것이 좋겠습니다.

-private String githubAccessToken; //깃허브 access_token 값
+@Column(name = "github_access_token", columnDefinition = "TEXT")
+private String githubAccessToken; //깃허브 access_token 값
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (1)

97-101: 문자열 비교 일관성 개선

createResponse 메서드에서는 toLowerCase()를 사용하는데, 여기서는 소문자로만 비교합니다. 일관성을 위해 대소문자 구분 없이 비교하는 것이 좋겠습니다.

-if ("github".equals(provider) && user.getGithubUrl()==null) {
+if ("github".equalsIgnoreCase(provider) && user.getGithubUrl() == null) {
     user.setGithubUrl(response.getGithubUrl());
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcf2f24 and 6cfda26.

📒 Files selected for processing (20)
  • build.gradle (2 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GithubOAuth2Response.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GoogleOAuth2Response.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/OAuth2Response.java (1 hunks)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java (0 hunks)
  • src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java (1 hunks)
  • src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java (4 hunks)
  • src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java (1 hunks)
  • src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java (0 hunks)
  • src/main/java/org/ezcode/codetest/common/security/util/SecurityPath.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/exception/code/AuthExceptionCode.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/CustomOAuth2User.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/User.java (5 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (2 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java (1 hunks)
  • src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java (1 hunks)
  • src/main/java/org/ezcode/codetest/presentation/usermanagement/OAuth2Controller.java (1 hunks)
  • src/main/resources/application.properties (3 hunks)
💤 Files with no reviewable changes (2)
  • src/main/java/org/ezcode/codetest/application/usermanagement/user/service/UserService.java
  • src/main/java/org/ezcode/codetest/common/security/util/JwtFilter.java
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GithubOAuth2Response.java (1)
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GoogleOAuth2Response.java (1)
  • Schema (7-52)
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (1)
src/main/java/org/ezcode/codetest/domain/user/model/entity/UserFactory.java (1)
  • UserFactory (7-30)
🪛 ast-grep (0.38.1)
src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java

[warning] 19-19: 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] 27-27: 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] 19-19: 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] 27-27: 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 (11)
build.gradle (1)

117-118: JAXB API 버전 확인이 필요합니다.

AES 암호화를 위해 추가된 JAXB API 2.3.1 버전이 최신 버전인지, 보안 취약점이 없는지 확인해주세요.

JAXB API 최신 버전과 2.3.1 버전의 보안 취약점 여부를 확인해주세요.
src/main/java/org/ezcode/codetest/domain/user/exception/code/AuthExceptionCode.java (1)

22-22: 토큰 인코딩 실패 예외 코드가 적절하게 추가되었습니다.

AES 암호화 실패 시 사용할 예외 코드가 기존 패턴을 따라 올바르게 구현되었습니다.

src/main/java/org/ezcode/codetest/common/security/config/SecurityConfig.java (1)

58-60: 매개변수 이름 변경으로 가독성이 개선되었습니다.

OAuth2 설정에서 람다 매개변수 이름을 userInfo로 변경하여 코드 가독성이 향상되었습니다.

src/main/java/org/ezcode/codetest/domain/user/repository/UserRepository.java (1)

23-23: GitHub 액세스 토큰 업데이트 메서드가 적절히 추가되었습니다.

GitHub OAuth 통합을 위한 토큰 업데이트 메서드가 리포지토리 패턴을 따라 올바르게 정의되었습니다.

src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/user/UserRepositoryImpl.java (1)

50-53: GitHub 액세스 토큰 업데이트 메서드가 올바르게 구현되었습니다.

JPA save를 통한 간단하고 효과적인 구현으로 기존 리포지토리 패턴을 일관성 있게 따르고 있습니다.

src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/GoogleOAuth2Response.java (1)

41-49: 인터페이스 확장에 대한 적절한 플레이스홀더 구현

Google OAuth2 응답에 GitHub 관련 데이터가 없는 상황에서 빈 문자열을 반환하는 것은 적절한 구현입니다. 인터페이스 계약을 올바르게 준수하고 있습니다.

src/main/java/org/ezcode/codetest/domain/user/service/UserDomainService.java (1)

131-134: GitHub 액세스 토큰 업데이트 메서드 구현 승인

메서드가 기존 클래스 패턴을 잘 따르고 있습니다:

  • 적절한 로깅 추가
  • 리포지토리 계층으로의 올바른 위임
  • 다른 메서드들과 일관된 구조
src/main/java/org/ezcode/codetest/application/usermanagement/user/dto/response/OAuth2Response.java (1)

16-20: 인터페이스 확장 구현 승인

GitHub 특화 메서드 추가가 깔끔하게 구현되었습니다:

  • 명확한 메서드 명명과 주석
  • 단일 책임 원칙 준수
  • 다양한 OAuth 제공자에 대한 다형성 지원
src/main/java/org/ezcode/codetest/common/security/util/SecurityPath.java (1)

8-10: OAuth2 엔드포인트에 대한 보안 경로 설정 승인

OAuth2 컨트롤러 지원을 위한 public path 추가가 적절히 구현되었습니다:

  • 기존 OAuth 경로에 명확한 주석 추가
  • 새로운 API OAuth2 경로 추가
  • OAuth 플로우에 필요한 공개 접근 허용
src/main/java/org/ezcode/codetest/domain/user/service/CustomOAuth2UserService.java (2)

34-56: 리팩토링이 잘 되었습니다!

loadUser 메서드가 메서드 추출을 통해 가독성이 크게 향상되었습니다. 각 메서드가 단일 책임을 가지도록 잘 분리되었네요.


87-96: 기존 유저의 소셜 로그인 추가 처리가 잘 구현되었습니다

기존 유저가 처음으로 소셜 로그인을 추가할 때 verified 상태와 reviewToken을 부여하는 로직이 PR 목적에 맞게 잘 구현되었습니다.

Comment on lines +26 to +33
public String decrypt(String encryptedInput) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedInput);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

복호화 메서드도 보안 모드로 업데이트 필요

암호화 메서드와 동일하게 CBC 모드를 사용하도록 수정해야 합니다.

-public String decrypt(String encryptedInput) throws Exception {
-    SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
-    Cipher cipher = Cipher.getInstance(ALGORITHM);
-    cipher.init(Cipher.DECRYPT_MODE, keySpec);
-    byte[] encryptedBytes = Base64.getDecoder().decode(encryptedInput);
-    byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
-    return new String(decryptedBytes);
+public String decrypt(String encryptedInput) throws Exception {
+    if (SECRET_KEY.getBytes().length != 16 && SECRET_KEY.getBytes().length != 24 && SECRET_KEY.getBytes().length != 32) {
+        throw new IllegalArgumentException("AES key must be 16, 24, or 32 bytes");
+    }
+    
+    byte[] combined = Base64.getDecoder().decode(encryptedInput);
+    
+    // IV 추출 (처음 16바이트)
+    byte[] iv = new byte[16];
+    byte[] encryptedBytes = new byte[combined.length - 16];
+    System.arraycopy(combined, 0, iv, 0, 16);
+    System.arraycopy(combined, 16, encryptedBytes, 0, encryptedBytes.length);
+    
+    SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
+    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+    IvParameterSpec ivSpec = new IvParameterSpec(iv);
+    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+    
+    byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+    return new String(decryptedBytes, "UTF-8");
 }

필요한 import 추가:

+import javax.crypto.spec.IvParameterSpec;
+import java.nio.charset.StandardCharsets;
📝 Committable suggestion

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

Suggested change
public String decrypt(String encryptedInput) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedInput);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
// Add at top of the file:
+ import javax.crypto.spec.IvParameterSpec;
+ import java.nio.charset.StandardCharsets;
public String decrypt(String encryptedInput) throws Exception {
// Validate key length
int keyLen = SECRET_KEY.getBytes().length;
if (keyLen != 16 && keyLen != 24 && keyLen != 32) {
throw new IllegalArgumentException("AES key must be 16, 24, or 32 bytes");
}
// Decode IV + ciphertext
byte[] combined = Base64.getDecoder().decode(encryptedInput);
// Extract IV (first 16 bytes)
byte[] iv = new byte[16];
byte[] encryptedBytes = new byte[combined.length - 16];
System.arraycopy(combined, 0, iv, 0, 16);
System.arraycopy(combined, 16, encryptedBytes, 0, encryptedBytes.length);
// Initialize cipher in CBC mode with PKCS5 padding
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
🧰 Tools
🪛 ast-grep (0.38.1)

[warning] 27-27: 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] 27-27: 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)

🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java around
lines 26 to 33, the decrypt method uses the default cipher mode instead of CBC
mode like the encrypt method. Update the Cipher.getInstance call to specify
"AES/CBC/PKCS5Padding" to match the encryption mode. Also, initialize the cipher
with an IvParameterSpec for the CBC mode using the same IV as encryption. Add
necessary imports for IvParameterSpec and any other required classes.

Comment on lines +18 to +24
public String encrypt(String input) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

치명적인 보안 취약점: ECB 모드 사용

AES ECB 모드는 보안상 안전하지 않습니다. 같은 평문이 같은 암호문을 생성하므로 패턴 분석이 가능합니다. CBC 또는 GCM 모드를 사용해야 합니다.

-public String encrypt(String input) throws Exception {
-    SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
-    Cipher cipher = Cipher.getInstance(ALGORITHM);
-    cipher.init(Cipher.ENCRYPT_MODE, keySpec);
-    byte[] encryptedBytes = cipher.doFinal(input.getBytes());
-    return Base64.getEncoder().encodeToString(encryptedBytes);
+public String encrypt(String input) throws Exception {
+    if (SECRET_KEY.getBytes().length != 16 && SECRET_KEY.getBytes().length != 24 && SECRET_KEY.getBytes().length != 32) {
+        throw new IllegalArgumentException("AES key must be 16, 24, or 32 bytes");
+    }
+    
+    SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
+    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+    cipher.init(Cipher.ENCRYPT_MODE, keySpec);
+    
+    byte[] iv = cipher.getIV();
+    byte[] encryptedBytes = cipher.doFinal(input.getBytes("UTF-8"));
+    
+    // IV와 암호문을 함께 저장
+    byte[] combined = new byte[iv.length + encryptedBytes.length];
+    System.arraycopy(iv, 0, combined, 0, iv.length);
+    System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
+    
+    return Base64.getEncoder().encodeToString(combined);
 }
📝 Committable suggestion

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

Suggested change
public String encrypt(String input) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = cipher.doFinal(input.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public String encrypt(String input) throws Exception {
// Ensure the key is a valid AES length
if (SECRET_KEY.getBytes().length != 16
&& SECRET_KEY.getBytes().length != 24
&& SECRET_KEY.getBytes().length != 32) {
throw new IllegalArgumentException("AES key must be 16, 24, or 32 bytes");
}
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// Retrieve the randomly generated IV
byte[] iv = cipher.getIV();
byte[] encryptedBytes = cipher.doFinal(input.getBytes("UTF-8"));
// Prepend IV to ciphertext so it can be used for decryption
byte[] combined = new byte[iv.length + encryptedBytes.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length);
return Base64.getEncoder().encodeToString(combined);
}
🧰 Tools
🪛 ast-grep (0.38.1)

[warning] 19-19: 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] 19-19: 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)

🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java around
lines 18 to 24, the encryption method uses AES in ECB mode, which is insecure
due to pattern leakage. Change the Cipher instance to use AES in CBC or GCM
mode, initialize it with an IV (initialization vector), and update the
encryption logic to handle the IV properly. Ensure the key and IV are securely
generated and managed.

Comment on lines +36 to +47
public void redirectToProvider(
@PathVariable String provider,
HttpServletRequest request,
HttpServletResponse response,
@RequestParam(required = false) String redirect_uri
) throws IOException {
if (redirect_uri != null) {
request.getSession().setAttribute("redirect_uri", redirect_uri);
}

response.sendRedirect("/oauth2/authorization/" + provider);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

입력 검증 및 보안 강화 필요

현재 구현에서 provider 파라미터와 redirect_uri에 대한 검증이 없어 보안 위험이 있습니다.

+private static final Set<String> ALLOWED_PROVIDERS = Set.of("github", "google");
+private static final Pattern REDIRECT_URI_PATTERN = Pattern.compile("^https?://[a-zA-Z0-9.-]+(/.*)?$");
+
 @GetMapping("/authorize/{provider}")
 public void redirectToProvider(
     @PathVariable String provider,
     HttpServletRequest request,
     HttpServletResponse response,
     @RequestParam(required = false) String redirect_uri
 ) throws IOException {
+    // Provider 검증
+    if (!ALLOWED_PROVIDERS.contains(provider.toLowerCase())) {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unsupported OAuth2 provider");
+        return;
+    }
+    
+    // Redirect URI 검증 (제공된 경우)
+    if (redirect_uri != null) {
+        if (!REDIRECT_URI_PATTERN.matcher(redirect_uri).matches()) {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid redirect URI format");
+            return;
+        }
+        request.getSession().setAttribute("redirect_uri", redirect_uri);
+    }
+    
-    if (redirect_uri != null) {
-        request.getSession().setAttribute("redirect_uri", redirect_uri);
-    }
-
-    response.sendRedirect("/oauth2/authorization/" + provider);
+    response.sendRedirect("/oauth2/authorization/" + provider.toLowerCase());
 }

필요한 import 추가:

+import java.util.Set;
+import java.util.regex.Pattern;
+import jakarta.servlet.http.HttpServletResponse;
🤖 Prompt for AI Agents
In
src/main/java/org/ezcode/codetest/presentation/usermanagement/OAuth2Controller.java
around lines 36 to 47, the method redirectToProvider lacks validation for the
provider and redirect_uri parameters, posing security risks. Add input
validation to ensure the provider matches allowed OAuth2 providers and validate
redirect_uri to prevent open redirect vulnerabilities. Also, include necessary
imports for validation utilities or annotations used.

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 (2)
src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java (2)

18-24: 치명적인 보안 취약점: ECB 모드 사용 (이전 리뷰와 동일한 이슈)

AES ECB 모드는 보안상 안전하지 않습니다. 동일한 평문이 동일한 암호문을 생성하므로 패턴 분석이 가능합니다. 이전 리뷰 코멘트에서 제시된 CBC 모드로 변경해야 합니다.


26-33: 복호화 메서드도 보안 모드로 업데이트 필요 (이전 리뷰와 동일한 이슈)

암호화 메서드와 동일하게 CBC 모드를 사용하도록 수정해야 합니다. 이전 리뷰에서 제시된 해결책을 적용해주세요.

🧹 Nitpick comments (1)
src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java (1)

18-33: 예외 처리 개선 필요

현재 메서드들이 일반적인 Exception을 던지고 있어 호출자가 구체적인 오류 상황을 파악하기 어렵습니다. 더 구체적인 예외 타입을 사용하거나 커스텀 예외를 정의하는 것이 좋습니다.

-public String encrypt(String input) throws Exception {
+public String encrypt(String input) throws IllegalArgumentException, GeneralSecurityException {
    // 구현 내용
}

-public String decrypt(String encryptedInput) throws Exception {
+public String decrypt(String encryptedInput) throws IllegalArgumentException, GeneralSecurityException {
    // 구현 내용
}

필요한 import 추가:

+import java.security.GeneralSecurityException;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cfda26 and edd727c.

📒 Files selected for processing (4)
  • src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java (4 hunks)
  • src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java (1 hunks)
  • src/main/java/org/ezcode/codetest/domain/user/exception/code/AuthExceptionCode.java (1 hunks)
  • src/main/resources/application.properties (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/resources/application.properties
  • src/main/java/org/ezcode/codetest/common/security/hander/CustomSuccessHandler.java
🧰 Additional context used
🪛 ast-grep (0.38.1)
src/main/java/org/ezcode/codetest/common/security/util/AESUtil.java

[warning] 19-19: 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] 27-27: 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] 19-19: 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] 27-27: 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)

🔇 Additional comments (2)
src/main/java/org/ezcode/codetest/domain/user/exception/code/AuthExceptionCode.java (2)

22-23: 새로운 예외 코드 추가 확인

OAuth2 구현과 AES 암호화 기능을 위한 새로운 예외 코드들이 적절히 추가되었습니다.

  • TOKEN_ENCODE_FAIL: AES 암호화 실패 시 사용
  • REDIRECT_URI_NOT_FOUND: OAuth2 리다이렉트 URI 누락 시 사용

두 예외 모두 적절한 HTTP 상태 코드(BAD_REQUEST)와 명확한 한국어 메시지를 가지고 있습니다.


21-23: ```shell
#!/bin/bash
echo "=== AESUtil references ==="
rg --type java "AESUtil"

echo -e "\n=== encodedGithubToken context ==="
rg -A5 -B5 "encodedGithubToken" --type java


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@pokerbearkr pokerbearkr merged commit 0a48b2a into dev Jun 26, 2025
2 checks passed
@pokerbearkr pokerbearkr deleted the feature/githubOAuth branch June 26, 2025 11:07
@coderabbitai coderabbitai bot mentioned this pull request Jul 28, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants