Skip to content

Sisc1 209 OAuthSuccessHandler 수정 및 CustomOidcUserService 추가 - #111

Merged
Kosw6 merged 8 commits into
mainfrom
SISC1-209-BE-redirect-url-프론트로-전달
Nov 15, 2025

Hidden character warning

The head ref may contain hidden characters: "SISC1-209-BE-redirect-url-\ud504\ub860\ud2b8\ub85c-\uc804\ub2ec"
Merged

Kosw6 merged 8 commits into
mainfrom
SISC1-209-BE-redirect-url-프론트로-전달

Conversation

@msciki7

@msciki7 msciki7 commented Nov 15, 2025 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • 신기능

    • OIDC 로그인 흐름 및 여러 OAuth 제공자(구글·카카오·깃허브) 일관 지원 추가
    • OIDC 사용자 정보 확장으로 로그인 시 이메일·이름 처리 개선
  • 버그 수정

    • 액세스/리프레시 토큰을 URL이 아닌 HttpOnly 쿠키로 안전하게 전달
    • OAuth 계정 연동을 데이터베이스 기반으로 안정화
    • 토큰 만료 정책(액세스 1시간, 리프레시 2주) 적용 및 관련 처리 개선

@coderabbitai

coderabbitai Bot commented Nov 15, 2025 •

Copy link
Copy Markdown

Walkthrough

OAuth/OIDC 인증 흐름을 통합·정리하고 AuthProvider 매핑을 추가했으며, OIDC 전용 서비스와 DB 기반 OAuth 계정 조회를 도입하고, 성공 핸들러에서 토큰 전달을 URL에서 HttpOnly 쿠키로 변경했습니다.

Changes

Cohort / File(s) 변경 요약
AuthProvider 확장
backend/src/main/java/org/sejongisc/backend/auth/entity/AuthProvider.java
문자열 제공자명을 enum으로 매핑하는 public static AuthProvider from(String providerName) 메서드 추가; enum 선언문(구문) 조정
OAuth2 사용자 서비스 통합
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOAuth2UserService.java
provider별 분기 로직을 switch로 정리; 공통 attributes 맵(provider, providerUid, email, name, userId)으로 통합; AuthProvider.from(...) 호출 사용; 디버그 로깅 조건부 추가
OIDC 사용자 서비스 추가
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOidcUserService.java
새 클래스 추가: CustomOidcUserService(OidcUserService 확장) 및 loadUser() 오버라이드로 attributes에 provider/providerUid/email/name 주입
성공 핸들러·토큰/계정 처리
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java
DB 기반 UserOauthAccount 조회(provider+providerUid)로 사용자 획득 변경; 액세스/리프레시 토큰을 HttpOnly 쿠키( SameSite=Lax, secure=false 개발용 )로 설정; 미지원 제공자/매핑 누락 시 에러 처리
시큐리티 설정 변경
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java
CustomOidcUserService 의존성 추가 및 userInfoEndpoint에 userService와 oidcUserService 모두 등록; 인증/허용 경로 규칙 일부 조정 (/login/**, /oauth2/** 공개, /api/user/** 인증 필요 등)

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browser
    participant OAuthProvider as OAuth2/OIDC Provider
    participant Backend
    participant DB
    participant Frontend

    User->>Browser: 소셜 로그인 시작
    Browser->>OAuthProvider: 인증/동의
    OAuthProvider->>Browser: 코드/토큰
    Browser->>Backend: 콜백 요청 (code, provider)

    Note over Backend: CustomOAuth2UserService / CustomOidcUserService
    Backend->>Backend: 사용자 속성 매핑 (provider, providerUid, email, name)
    Backend->>DB: findByProviderAndProviderUid(provider, providerUid)
    DB-->>Backend: UserOauthAccount + User

    Note over Backend: OAuth2SuccessHandler
    Backend->>Backend: 액세스/리프레시 토큰 생성
    Backend->>Browser: HttpOnly 쿠키로 토큰 설정 + 302 리다이렉트
    Browser->>Frontend: 리다이렉트 (쿠키 자동 전송)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60분

  • 집중 검토 대상:
    • OAuth2SuccessHandler.java: DB 조회 경로, 쿠키 속성(secure, sameSite, HttpOnly), 에러 처리
    • CustomOAuth2UserService.java: provider별 providerUid/email/name 추출 일관성
    • CustomOidcUserService.java: OIDC attributes 오버라이드 영향
    • SecurityConfig.java: 권한 규칙 변경 영향 검증

Possibly related PRs

Suggested reviewers

  • discipline24

Poem

🐰
제공자들 사이를 깡충깡충 뛰며,
키와 토큰을 쿠키에 숨겼네.
switch로 길을 정리하고,
DB에 계정 달아놓으니,
로그인 흐름이 환히 웃네 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 주요 변경사항인 OAuthSuccessHandler 수정 및 CustomOidcUserService 추가를 정확히 요약하고 있으며, 파일 리스트나 불필요한 표현 없이 명확하고 간결합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-209-BE-redirect-url-프론트로-전달

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (8)
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOAuth2UserService.java (2)

56-75: 소셜 provider별 attribute 파싱 시 null/타입 가정이 강합니다

  • kakao 분기에서 kakao_account, profile을 바로 캐스팅/접근하고 있어 응답 스키마가 바뀌거나 권한이 부족한 경우 NullPointerException 가능성이 있습니다.
  • github 분기에서 email은 퍼블릭 이메일이 없거나 scope가 부족하면 null이 될 수 있습니다.
  • google 분기에서도 sub, email, name이 없을 때의 방어 로직이 없습니다.

최소한 다음 정도는 고려해 보시면 좋겠습니다.

  • 중간 단계에서 kakaoAccount == null / profile == null인 경우 의미 있는 예외 메시지로 실패 처리.
  • email이 null일 수 있는 provider(Kakao/GitHub)에 대해서는, 회원 가입 정책(이메일 필수 여부)에 맞춰 fallback 전략 또는 에러 처리 분기 추가.
  • 예외 타입도 RuntimeException 대신 도메인에 맞는 커스텀 예외나 OAuth2AuthenticationException 계열로 통일하면 디버깅이 수월합니다.

107-118: GrantedAuthority가 User.role과 분리돼 있어 향후 롤 확장 시 불일치 위험이 있습니다

현재는 항상 new SimpleGrantedAuthority("ROLE_TEAM_MEMBER")로 권한을 고정하고 있는데, DB의 User.role 값과 불일치할 수 있습니다. 나중에 ADMIN, LEADER 등 롤이 늘어나면 버그로 이어질 가능성이 큽니다.

예를 들어 다음처럼 User.role을 기반으로 권한을 구성하는 쪽이 더 안전해 보입니다(실제 enum/필드 이름에 맞게 조정 필요).

-        return new DefaultOAuth2User(
-                List.of(new SimpleGrantedAuthority("ROLE_TEAM_MEMBER")),
-                attributes,
-                "userId"
-        );
+        var authority = new SimpleGrantedAuthority("ROLE_" + user.getRole().name());
+        return new DefaultOAuth2User(
+                List.of(authority),
+                attributes,
+                "userId"
+        );
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOidcUserService.java (1)

36-55: OIDC attributes 확장 방식 자체는 괜찮지만, claims 사용 시 동작 차이를 인지해 두는 게 좋습니다

현재 구현은

  • original = oidcUser.getAttributes()를 복사한 뒤, provider, providerUid, email, name을 추가하고,
  • 익명 서브클래스로 DefaultOidcUser를 생성하여 getAttributes()만 override하고 있습니다.

이렇게 하면

  • OAuth2SuccessHandler처럼 getAttributes()를 사용하는 코드에서는 커스텀 필드를 정상적으로 볼 수 있지만,
  • OidcUser#getClaims()나 getUserInfo()를 직접 사용하는 코드에서는 여전히 원래 claims만 보이고 provider 정보는 보이지 않을 수 있습니다.

당장은 문제가 아니지만, 이후 OIDC 관련 로직이 추가될 때

  • attributes 기반으로만 읽는지,
  • claims/userInfo를 직접 쓰는 코드가 생기지 않는지

를 맞춰 주면 혼동을 줄일 수 있습니다. 필요하다면 OidcUserInfo를 새로 만들어 provider 등을 포함시키는 방식도 고려해 볼 수 있습니다.

backend/src/main/java/org/sejongisc/backend/auth/entity/AuthProvider.java (1)

8-15: AuthProvider.from 헬퍼 추가는 좋고, OAuth2SuccessHandler에서 재사용하면 중복을 줄일 수 있습니다

  • providerName.toLowerCase()를 기준으로 "google", "kakao", "github"을 enum으로 매핑하는 구현은 명확하고 사용하기 좋습니다.
  • 현재 OAuth2SuccessHandler에서도 동일한 switch를 다시 작성하고 있으므로, 거기서는 단순히 AuthProvider.from(providerStr)를 호출하도록 변경하면 중복을 없앨 수 있습니다.

추가로, 외부 입력에서 null이 들어올 가능성이 있다면 providerName == null에 대한 방어 로직을 넣고 조금 더 친절한 예외 메시지를 주는 것도 고려해 볼 수 있습니다.

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

71-99: 현재 보안 설정에서는 /api/user/**만 인증이 필요하고, 나머지 요청은 모두 공개입니다

이 블록을 보면:

  • 여러 인증/회원 관련 엔드포인트 + /login/**, /oauth2/**, /webjars/**는 permitAll(),
  • /api/user/**는 authenticated(),
  • 그 외는 .anyRequest().permitAll() 로 구성돼 있습니다.

Spring Security의 매칭 순서 상 /api/user/**는 authenticated로 잘 보호되겠지만, 그 외 API (/api/**의 다른 경로 등)는 모두 공개 접근이 허용됩니다.

서비스 보안 요구사항이

  • “회원 정보만 보호하고 나머지는 공개 API”라면 괜찮지만,
  • 더 많은 엔드포인트를 보호해야 한다면 .anyRequest().authenticated() 또는 특정 prefix(/api/** 등)에 대한 추가 rule이 필요할 수 있습니다.

의도된 정책인지 한 번 더 확인해 보시는 것을 권장드립니다.

backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (3)

64-70: AuthProvider 매핑이 AuthProvider.from(String)과 중복됩니다

현재:

AuthProvider provider =
        switch (providerStr) {
            case "kakao" -> AuthProvider.KAKAO;
            case "github" -> AuthProvider.GITHUB;
            case "google" -> AuthProvider.GOOGLE;
            default -> throw new IllegalStateException("Unknown OAuth provider: " + providerStr);
        };

이미 AuthProvider.from(String providerName)이 존재하므로, 다음처럼 단순화하면 중복을 줄이고 한 곳에서만 매핑을 관리할 수 있습니다.

-        AuthProvider provider =
-                switch (providerStr) {
-                    case "kakao" -> AuthProvider.KAKAO;
-                    case "github" -> AuthProvider.GITHUB;
-                    case "google" -> AuthProvider.GOOGLE;
-                    default -> throw new IllegalStateException("Unknown OAuth provider: " + providerStr);
-                };
+        AuthProvider provider = AuthProvider.from(providerStr);

98-112: 쿠키 보안 설정 및 리다이렉트 URL을 환경 설정으로 분리하는 것을 권장합니다

현재:

  • secure(false)로 설정되어 있고 주석에 “로컬 개발”이라고 되어 있지만, 이 설정이 그대로 운영에도 적용될 수 있습니다.
  • sameSite("Lax")로 고정되어 있어, 배포 환경(도메인/프론트 도메인 분리 등)에 따라 CORS/Cookie 이슈가 생길 수 있습니다.
  • 리다이렉트 URL이 "http://localhost:5173/oauth/success"로 하드코딩돼 있습니다.

운영 환경까지 고려하면 다음을 추천드립니다.

  • secure, sameSite, 리다이렉트 URL(및 쿠키 도메인)을 application-*.yml 설정값으로 분리.
  • 프로파일(로컬/스테이징/실서버)에 따라
    • 로컬: secure=false, SameSite=Lax 또는 None (+ http),
    • 운영: secure=true, HTTPS 도메인, 필요한 경우 SameSite=None + CORS 설정
      로 다르게 가져가도록 구성.

지금 단계에서는 로컬 개발을 우선하셨겠지만, 나중에 배포 시 이 부분을 꼭 챙겨 주셔야 할 것 같습니다.


120-127: 리다이렉트 URL 하드코딩 대신 설정 기반으로 전환을 고려해 보세요

String redirectUrl = "http://localhost:5173/oauth/success";

로그인 성공 후 프론트 주소가 바뀌거나, 환경(로컬/스테이징/운영)별로 다른 URL을 사용해야 할 때마다 코드를 수정/배포해야 합니다.

@Value 또는 설정 프로퍼티(@ConfigurationProperties)로 redirect base URL을 주입받아 사용하면,

  • 환경별 설정만 변경해도 되고,
  • 테스트에서도 더 유연하게 핸들링할 수 있습니다.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0c8fdc and e88792b.

📒 Files selected for processing (5)
  • backend/src/main/java/org/sejongisc/backend/auth/entity/AuthProvider.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOAuth2UserService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOidcUserService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (4 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOidcUserService.java (2)
backend/src/main/java/org/sejongisc/backend/common/auth/config/CustomOAuth2UserService.java (1)
  • Slf4j (25-121)
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (1)
  • Slf4j (32-130)
🔇 Additional comments (2)
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (1)

56-69: OAuth2/OIDC userInfoEndpoint 설정은 합리해 보이지만, Google이 실제로 OIDC로 들어오는지 확인이 필요합니다

현재 설정:

.userInfoEndpoint(u -> {
    u.userService(customOAuth2UserService);   // kakao, github
    u.oidcUserService(customOidcUserService); // google
})
  • Spring Security에서는 클라이언트 registration이 OIDC인 경우 oidcUserService가, 그 외에는 userService가 사용됩니다.
  • 즉, Google registration이 OIDC로 설정되어 있어야 CustomOidcUserService가 실제로 사용되고, 그 안에서 세팅한 provider, providerUid가 OAuth2SuccessHandler까지 전달됩니다.

구성 파일(yaml/properties)의 Google client registration이 OIDC로 설정되어 있는지 한 번만 확인해 두시면 좋겠습니다.

backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (1)

50-52: 검토 의견의 주요 전제가 Spring Security 클래스 계층 구조와 맞지 않습니다

검토 의견에서는 DefaultOidcUser가 DefaultOAuth2User를 상속하지 않는다고 주장하지만, Spring Security 공식 문서에 따르면 DefaultOidcUser는 실제로 DefaultOAuth2User를 확장(extends)합니다.

실제 클래스 계층:

  • DefaultOidcUser는 DefaultOAuth2User를 확장하고 OidcUser를 구현합니다
  • 따라서 instanceof DefaultOAuth2User 검사는 DefaultOidcUser 인스턴스에 대해 성공합니다
  • DefaultOidcUser는 OIDC 인증용 DefaultOAuth2User의 확장 구현입니다

검증된 코드:

  • CustomOidcUserService는 DefaultOidcUser 반환 (line 44-50)
  • CustomOAuth2UserService는 DefaultOAuth2User 반환 (line 92-96)
  • 현재 instanceof 검사는 두 경우 모두 성공합니다

검토 의견의 핵심 주장인 "OIDC 로그인 시 항상 IllegalStateException이 발생할 가능성"은 실제 클래스 계층 구조에서 발생하지 않습니다. 현재 코드는 OIDC 로그인도 올바르게 처리합니다.

Likely an incorrect or invalid review comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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/common/auth/config/OAuth2SuccessHandler.java (1)

106-114: CRITICAL: refreshCookie 설정이 중복되어 충돌하고 있습니다

Lines 108-111에서 secure와 sameSite 속성이 두 번 설정되어 있습니다:

  • Line 108-109: secure(false), sameSite("Lax")
  • Line 110-111: secure(true), sameSite("None")

이는 명백한 버그입니다. 또한 secure=false와 sameSite=None의 조합은 대부분의 브라우저에서 거부됩니다(SameSite=None은 Secure 쿠키여야 함).

로컬 개발 환경이라면 secure(false)와 sameSite("Lax")만 유지하고, 프로덕션에서는 환경 변수로 secure(true)와 sameSite("None")을 설정하도록 수정하세요.

 ResponseCookie refreshCookie = ResponseCookie.from("refresh", refreshToken)
         .httpOnly(true)
         .secure(false)
         .sameSite("Lax")
-        .secure(true)
-        .sameSite("None")
         .path("/")
         .maxAge(60L * 60 * 24 * 14) // 2 weeks
         .build();
♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (1)

76-82: 예외 처리 방식을 개선하세요

successHandler 내부에서 RuntimeException을 던지면 failureHandler가 처리하지 못하고 500 에러가 발생하여 프론트엔드의 /oauth/fail 핸들링이 작동하지 않습니다. 이전 리뷰에서 이미 지적된 사항입니다.

try-catch 블록으로 예외를 감싸고 에러 발생 시 명시적으로 리다이렉트하거나, OAuth2 인증 단계에서 OAuth2AuthenticationException을 던지도록 수정하세요.

+try {
     UserOauthAccount account = userOauthAccountRepository
             .findByProviderAndProviderUid(provider, providerUid)
             .orElseThrow(() -> new RuntimeException("소셜 계정이 DB에 없습니다. (회원가입 필요)"));

     User user = userRepository.findById(account.getUser().getUserId())
             .orElseThrow(() -> new RuntimeException("User not found"));
+    
+    // JWT 생성 및 쿠키 설정 로직...
+    
+} catch (Exception e) {
+    log.error("[OAuth2SuccessHandler] Error during authentication success handling", e);
+    getRedirectStrategy().sendRedirect(request, response, 
+        "http://localhost:5173/oauth/fail?error=account_not_found");
+    return;
+}
🧹 Nitpick comments (3)
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (3)

17-17: 사용하지 않는 import를 제거하세요

OidcUser를 import했지만 코드에서 사용하지 않고 있습니다.

-import org.springframework.security.oauth2.core.oidc.user.OidcUser;

122-122: 리다이렉트 URL을 설정 파일로 외부화하세요

http://localhost:5173/oauth/success가 하드코딩되어 있습니다. 프로덕션 환경에서는 다른 URL을 사용해야 하므로 환경 변수나 설정 파일로 관리하는 것이 좋습니다.

@Value("${oauth.success.redirect-url}")
private String oauthSuccessRedirectUrl;

// ...

String redirectUrl = oauthSuccessRedirectUrl;

48-48: 주석 처리된 로그를 정리하세요

디버깅에 필요하다면 활성화하고, 불필요하다면 제거하는 것이 코드 가독성에 좋습니다.

Also applies to: 73-73, 127-127

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e88792b and 5caedde.

📒 Files selected for processing (1)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (6 hunks)
🔇 Additional comments (1)
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java (1)

50-52: LGTM!

패턴 매칭을 사용한 타입 체크가 올바르게 구현되어 있습니다.

Comment on lines +55 to +58
Map<String, Object> attrs = oauthUser.getAttributes();

String providerStr = (String) attrs.get("provider");
String providerUid = (String) attrs.get("providerUid");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

CustomOAuth2UserService와 CustomOidcUserService가 필수 속성을 설정하는지 확인하세요

이 핸들러는 attributes에 provider와 providerUid가 존재한다고 가정합니다. 이 값들이 OAuth2/OIDC 사용자 서비스에서 올바르게 설정되는지 확인이 필요합니다.

다음 스크립트를 실행하여 사용자 서비스에서 이러한 속성을 설정하는지 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Verify that CustomOAuth2UserService and CustomOidcUserService set provider and providerUid attributes

# Search for where provider and providerUid are added to attributes
rg -nP --type=java -C5 '(put\("provider"|put\("providerUid"|provider.*=|providerUid.*=)' 

# Find CustomOAuth2UserService and CustomOidcUserService implementations
fd -t f "(CustomOAuth2UserService|CustomOidcUserService).java" -x cat {}

Length of output: 24333


providerUid의 null 검증 누락

CustomOAuth2UserService와 CustomOidcUserService는 속성을 올바르게 설정하고 있습니다. 그러나 OAuth2SuccessHandler의 60-62줄에서 provider null 검증은 있지만, providerUid의 null 검증이 누락되어 있습니다.

77줄의 findByProviderAndProviderUid(provider, providerUid) 호출 전에 providerUid null 검증을 추가하세요.

if (providerStr == null) {
    throw new IllegalStateException("OAuth provider attribute missing from attributes");
}

if (providerUid == null) {
    throw new IllegalStateException("OAuth providerUid attribute missing from attributes");
}
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java
around lines 55-77, the code checks provider for null but omits a null check for
providerUid before calling findByProviderAndProviderUid; add a null validation
for providerUid (mirror the existing provider check) and throw an
IllegalStateException with a clear message like "OAuth providerUid attribute
missing from attributes" when providerUid is null so the subsequent
findByProviderAndProviderUid call is safe.

Comment on lines +57 to +70
String providerStr = (String) attrs.get("provider");
String providerUid = (String) attrs.get("providerUid");

if (providerStr == null) {
throw new IllegalStateException("OAuth provider attribute missing from attributes");
}

AuthProvider provider =
switch (providerStr) {
case "kakao" -> AuthProvider.KAKAO;
case "github" -> AuthProvider.GITHUB;
case "google" -> AuthProvider.GOOGLE;
default -> throw new IllegalStateException("Unknown OAuth provider: " + providerStr);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

providerUid에 대한 null 체크를 추가하세요

providerStr은 null 체크를 하고 있지만, providerUid는 null 체크가 누락되어 있습니다. providerUid가 null인 경우 Line 77의 DB 조회에서 예기치 않은 동작이 발생할 수 있습니다.

 String providerStr = (String) attrs.get("provider");
 String providerUid = (String) attrs.get("providerUid");

 if (providerStr == null) {
     throw new IllegalStateException("OAuth provider attribute missing from attributes");
 }
+if (providerUid == null) {
+    throw new IllegalStateException("OAuth providerUid attribute missing from attributes");
+}

 AuthProvider provider =
         switch (providerStr) {
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java
around lines 57 to 70, add a null-check for providerUid like the existing check
for providerStr and throw an IllegalStateException with a clear message if
providerUid is null (e.g., "OAuth provider UID attribute missing from
attributes"); ensure this check occurs before any DB lookup or further
processing so you fail-fast and avoid unexpected behavior when providerUid is
missing.

Comment on lines 98 to 104
ResponseCookie accessCookie = ResponseCookie.from("access", accessToken)
.httpOnly(true)
.secure(true)
.sameSite("None")
.secure(false) // 로컬 개발
.sameSite("Lax") // 로컬에서는 None 비추천
.path("/")
.maxAge(60L * 60) // 1 hour
.build();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

쿠키 보안 설정을 환경별로 구성 가능하도록 개선하세요

현재 secure(false)와 sameSite("Lax")가 하드코딩되어 있어 로컬 개발 환경에만 적합합니다. 프로덕션 배포 시 이 설정은 보안 취약점이 됩니다.

Spring 프로파일이나 환경 변수를 사용하여 환경별로 다른 설정을 적용하도록 수정하세요.

예시:

@Value("${cookie.secure:false}")
private boolean cookieSecure;

@Value("${cookie.sameSite:Lax}")
private String cookieSameSite;

// 쿠키 생성 시
ResponseCookie accessCookie = ResponseCookie.from("access", accessToken)
        .httpOnly(true)
        .secure(cookieSecure)
        .sameSite(cookieSameSite)
        .path("/")
        .maxAge(60L * 60)
        .build();
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/common/auth/config/OAuth2SuccessHandler.java
around lines 98-104, the cookie security options are hard-coded (secure(false),
sameSite("Lax")) which is unsafe for production; change to read these values
from configuration (e.g. @Value or Environment) with sensible defaults (secure
default true, sameSite default "Lax" or "None" per needs), replace the literal
calls with the injected variables when building the ResponseCookie, add
corresponding properties to application.properties/application-prod.properties
(and/or profiles) and validate/normalize the sameSite value before use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants