Skip to content

[feat] 소셜 로그인 사용자 정보 조회 api 개발 및 소셜 로그인 리다이렉트 분기 처리#177

Merged
seongjunnoh merged 13 commits into
developfrom
feat/#174-check-user-persist
Aug 8, 2025
Merged

[feat] 소셜 로그인 사용자 정보 조회 api 개발 및 소셜 로그인 리다이렉트 분기 처리#177
seongjunnoh merged 13 commits into
developfrom
feat/#174-check-user-persist

Conversation

@buzz0331

@buzz0331 buzz0331 commented Aug 8, 2025

Copy link
Copy Markdown
Contributor

#️⃣ 연관된 이슈

closes #174

📝 작업 내용

안드로이드
소셜 로그인 provider로부터 받은 식별자를 쿼리 파라미터로 받아 서버 DB에 존재하는지 여부를 판단하여 액세스 토큰 또는 임시 토큰을 전달하는 api를 구현했습니다.


기존 소셜 로그인 api를 유지하고 정적 redirect url을 통해 신규 유저 여부에 따라 홈화면 또는 회원가입 화면으로 redirect 시키도록 했습니다. 이때, 쿠키를 통해 토큰을 전달합니다.

📸 스크린샷

💬 리뷰 요구사항

토큰을 바디로 넘겨주는 것이 더 안전하다는 피드백을 받아 회원가입 api, 사용자 정보 조회 api에서 반환하는 토큰은 모두 바디로 넘겨주는 것으로 수정하였습니다.

하나의 간단한 api이기 때문에 따로 패키지를 만드는 것보다는 기존 security.oauth2Id 패키지 하위에 컨트롤러 하나만 선언하는 것이 낫다고 판단했습니다~

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

  • 신규 기능

    • 소셜 로그인 사용자를 위한 OAuth2 사용자 확인 엔드포인트(/oauth2/users) 추가.
    • 사용자 회원가입 결과에 accessToken 포함.
    • Spring Boot Actuator 의존성 추가.
  • 버그 수정

    • JWT 인증 필터와 보안 설정에서 /oauth2/users 경로 화이트리스트에 추가.
  • 개선 및 변경

    • OAuth2 인증 성공 시 토큰을 응답 헤더 대신 보안 쿠키로 전달하고, 신규/기존 사용자에 따라 리다이렉트 URL 분리.
    • 회원가입 컨트롤러에서 JWT 토큰을 응답 헤더에 직접 설정하지 않도록 변경.
    • 내부 DTO 및 서비스 반환 타입을 userId 단일값에서 userId와 accessToken을 포함하는 구조로 개선.
  • 문서화

    • OAuth2 사용자 확인 엔드포인트에 OpenAPI 문서 추가.

@coderabbitai

coderabbitai Bot commented Aug 8, 2025

Copy link
Copy Markdown

Walkthrough

OAuth2 소셜 로그인 신규 사용자 여부 확인을 위한 신규 REST 엔드포인트(/oauth2/users)가 추가되었습니다. JWT 토큰 발급 및 전달 방식이 기존 헤더/바디에서 쿠키 및 리다이렉트 방식으로 변경되었으며, 관련 DTO와 서비스 로직이 이에 맞게 수정 및 확장되었습니다. 일부 보안 및 CORS 설정도 함께 조정되었습니다.

Changes

Cohort / File(s) Change Summary
OAuth2 신규 사용자 확인 및 토큰 발급
src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java, src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenRequest.java, src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenResponse.java
신규 사용자 여부 확인을 위한 REST 컨트롤러, 요청/응답 DTO 추가.
OAuth2 인증 성공 처리 개선
src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java, src/main/java/konkuk/thip/common/security/constant/AuthParameters.java
인증 성공 시 토큰을 쿠키로 전달하고, 신규/기존 사용자에 따라 리다이렉트 URL 분기. 관련 상수 추가.
JWT 인증/보안 설정
src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java, src/main/java/konkuk/thip/config/SecurityConfig.java
/oauth2/users 경로 JWT 필터 및 인증 화이트리스트에 추가, CORS 헤더 노출 방식 개선.
회원가입 및 토큰 발급 로직 개선
src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java, src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java, src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java, src/main/java/konkuk/thip/user/application/service/UserSignupService.java, src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupResult.java
회원가입 시 accessToken을 함께 반환하도록 서비스 및 DTO 구조 변경. 컨트롤러에서 JWT 발급/헤더 처리 제거.
빌드/의존성
build.gradle
Spring Boot Actuator 의존성 추가.

Sequence Diagram(s)

sequenceDiagram
    participant Frontend
    participant CustomOAuth2UserController
    participant UserRepository
    participant JwtUtil

    Frontend->>CustomOAuth2UserController: POST /oauth2/users {oauth2Id}
    CustomOAuth2UserController->>UserRepository: findByOauth2Id(oauth2Id)
    alt User exists
        CustomOAuth2UserController->>JwtUtil: generateAccessToken(userId)
        CustomOAuth2UserController-->>Frontend: {token, isNewUser=false}
    else User not exists
        CustomOAuth2UserController->>JwtUtil: generateSignupToken(oauth2Id)
        CustomOAuth2UserController-->>Frontend: {token, isNewUser=true}
    end
Loading
sequenceDiagram
    participant OAuth2Provider
    participant CustomSuccessHandler
    participant HttpServletResponse

    OAuth2Provider->>CustomSuccessHandler: OAuth2 인증 성공 콜백
    CustomSuccessHandler->>CustomSuccessHandler: 신규/기존 사용자 판별
    alt 신규 사용자
        CustomSuccessHandler->>HttpServletResponse: Set-Cookie(signupToken)
        CustomSuccessHandler->>HttpServletResponse: Redirect /signup/genre
    else 기존 사용자
        CustomSuccessHandler->>HttpServletResponse: Set-Cookie(accessToken)
        CustomSuccessHandler->>HttpServletResponse: Redirect /feed
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
헤더에 담긴 액세스토큰을 통해 소셜 로그인 업체에 검증 (프론트에서 보낸 식별자 id = 소셜 로그인 업체의 식별자 id) 여부 판단 (#174) 소셜 로그인 업체에 직접 액세스토큰을 통해 검증하는 로직은 없고, DB 조회만 수행됨.
서버 유저 테이블에 존재하는 사용자인지 확인 (#174) /oauth2/users 엔드포인트에서 DB 조회로 신규/기존 사용자 여부를 판단함.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Spring Boot Actuator 의존성 추가 (build.gradle) 신규 사용자 여부 확인 및 소셜 로그인 관련 요구사항과 직접적인 관련이 없음. 운영/모니터링 목적의 의존성 추가로 보임.

Poem

🐇
소셜 로그인 토큰 춤,
신규/기존 구분해
쿠키에 담아 쏙!
리다이렉트로 점프하며
토끼도 신나네,
새로운 유저 환영해!
로그인 세상, 한층 더 안전해!

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#174-check-user-persist

🪧 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.
  • 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate unit tests to generate unit tests for 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.

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

🧹 Nitpick comments (11)
src/main/java/konkuk/thip/common/security/constant/AuthParameters.java (1)

15-16: 리다이렉트 URL 하드코딩 → 환경설정 외부화 권장

경로가 FE 라우팅/도메인 변화에 민감하므로 properties로 분리하는 편이 안전합니다. 예: thip.oauth2.redirect.signup-path, ...home-path@ConfigurationProperties로 주입 후 사용.

예시:

thip:
  oauth2:
    redirect:
      signup-path: "/signup/genre"
      home-path: "/feed"
src/main/java/konkuk/thip/config/SecurityConfig.java (1)

108-108: CORS exposedHeaders의 Authorization 노출 필요성 재검토

토큰을 응답 본문/쿠키로 전달한다면 Authorization 응답 헤더 노출은 불필요할 수 있습니다. 실제로 응답 헤더에 Authorization을 사용하지 않는다면 제거하여 최소권한 원칙을 지키는 것을 권장합니다.

- config.setExposedHeaders(Collections.singletonList(JWT_HEADER_KEY.getValue()));
+ // Authorization 헤더를 응답에 노출할 필요가 없으면 제거
+ // config.setExposedHeaders(Collections.singletonList(JWT_HEADER_KEY.getValue()));
src/main/java/konkuk/thip/common/security/oauth2/OAuth2TokenResponse.java (1)

1-10: 응답 스키마 확장/명확화 제안

  • token이 액세스 토큰/가입(임시) 토큰 중 무엇인지가 isNewUser로 암묵적으로 결정됩니다. 명시적 tokenType(e.g., ACCESS, SIGNUP) 또는 필드 분리(accessToken/signupToken)가 클라이언트 혼동을 줄입니다.
  • 만료 정보(expiresIn 또는 expiresAt)를 포함하면 클라이언트 캐싱/갱신 전략에 유용합니다.
  • boolean 컴포넌트명은 newUser로 두면 가독성이 조금 더 좋습니다(레코드 JSON 필드가 isNewUsernewUser로 단순화).
-public record OAuth2TokenResponse(
-        String token,
-        boolean isNewUser
-) {
-    public static OAuth2TokenResponse of(String token, boolean isNewUser) {
-        return new OAuth2TokenResponse(token, isNewUser);
-    }
-}
+public record OAuth2TokenResponse(
+        String token,
+        String tokenType, // "ACCESS" | "SIGNUP"
+        Long expiresIn,   // seconds, optional
+        boolean newUser
+) {
+    public static OAuth2TokenResponse of(String token, String tokenType, Long expiresIn, boolean newUser) {
+        return new OAuth2TokenResponse(token, tokenType, expiresIn, newUser);
+    }
+}
src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java (1)

4-4: 계약(Contract) 명세 보강 제안

UserSignupResult가 포함하는 의미(영속화된 userId, 발급된 accessToken의 유효시간/스코프 등)를 Javadoc에 명시하면 포트 사용자가 의도를 명확히 파악할 수 있습니다.

src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupResult.java (1)

3-10: 토큰 포함 DTO 반환 시 민감정보 취급 주의 및 문서화 제안

  • 이 DTO는 JWT를 포함하므로 서버/APM/액세스 로그에 직렬화되지 않도록 로깅 정책을 점검해 주세요.
  • OpenAPI 스키마에 간단한 설명/예시를 추가하면 클라이언트 연동에 도움이 됩니다.
src/main/java/konkuk/thip/user/application/service/UserSignupService.java (2)

29-38: 트랜잭션 경계 내부에서의 토큰 생성 시나리오 재검토

현재 @transactional 메서드 내부에서 사용자 저장 직후 토큰을 생성/반환합니다. 커밋 실패 시 이미 발급된 토큰이 외부에 전달될 수 있는(드물지만) 타이밍 이슈가 존재합니다. 아래 선택지 중 하나를 고려해 주세요.

  • TransactionSynchronizationManager.afterCommit에서 토큰 생성/세팅
  • 토큰 생성 책임을 트랜잭션 외부(상위 계층)로 이동
  • 토큰 생성 시 예외가 트랜잭션 롤백을 유발하지 않도록 별도 예외 처리

필수는 아니지만, 일관성 보장 관점에서 권장됩니다.


16-16: 애플리케이션 계층에서 adapter.out 패키지 의존 (레이어링 위반 가능성)

import static konkuk.thip.user.adapter.out.jpa.UserRole.USER;는 application → adapter(out) 방향 의존으로 보입니다. 도메인/공용 모델을 참조하도록 리팩터링하거나, 매핑은 어댑터 계층으로 밀어내는 것을 권장합니다.

src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java (2)

3-15: 불필요한 HttpServletResponse/IOException 제거

HttpServletResponse response 파라미터와 throws IOException은 사용되지 않습니다. 정리해 주세요.

적용 diff:

-import jakarta.servlet.http.HttpServletResponse;
@@
-            @RequestParam("oauth2Id") String oauth2Id,
-            HttpServletResponse response) throws IOException {
+            @RequestParam("oauth2Id") String oauth2Id) {

또는 POST로 전환 시 함께 제거 가능합니다.

Also applies to: 29-33


28-44: 토큰 응답의 캐시 방지 헤더 적용 권장

토큰을 바디로 반환하므로 중간 캐시 방지가 필요합니다. 전역 보안 헤더에서 no-store 적용을 권장합니다.

전역 설정 예시:

http.headers(headers -> headers.cacheControl(withDefaults())); // Cache-Control: no-store
src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java (2)

24-24: 쿠키 만료와 토큰 만료(30일) 간의 불일치 정리 제안

쿠키는 1일, JWT는 30일로 만료 정책이 불일치합니다. 의도라면 OK지만, 사용자 경험(빈번한 재로그인)과 보안정책을 고려해 정렬하거나 프로퍼티로 관리하는 것을 권장합니다.

아래처럼 Duration을 사용하고 의미를 명확히 하면 좋습니다:

-    private static final int COOKIE_MAX_AGE = 60 * 60 * 24; // 1일
+    private static final Duration COOKIE_MAX_AGE = Duration.ofDays(1); // 1일

위 변경 시 addTokenCookie에서 maxAge(COOKIE_MAX_AGE)로 그대로 사용할 수 있습니다(이전 코멘트의 ResponseCookie 예시 반영).


9-11: @slf4j 사용 여부 정리(미사용 애노테이션)

현재 로그 호출이 없어 @slf4j가 불필요합니다. 유지하려면 민감정보 없이 최소한의 브랜치 로그를 남기거나, 사용하지 않는다면 제거하세요.

예: 민감정보 없이 흐름만 기록

         if (oAuth2User.isNewUser()) {
+            log.info("OAuth2 success: new user → redirect signup");
             ...
         } else {
+            log.info("OAuth2 success: existing user → redirect home");
             ...
         }

토큰/식별자(oauth2Id, userId) 등 PII/민감정보는 로깅 금지 권장.

Also applies to: 19-19

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ded26f5 and 85c9800.

📒 Files selected for processing (11)
  • build.gradle (1 hunks)
  • src/main/java/konkuk/thip/common/security/constant/AuthParameters.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/OAuth2TokenResponse.java (1 hunks)
  • src/main/java/konkuk/thip/config/SecurityConfig.java (3 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupResult.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/service/UserSignupService.java (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java (2)
src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserService.java (1)
  • Slf4j (20-57)
src/main/java/konkuk/thip/common/security/util/JwtUtil.java (1)
  • Slf4j (20-89)
🪛 ast-grep (0.38.6)
src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java

[warning] 58-58: 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 Token or Double Submit Cookie patterns with defense-in-depth mechanisms such as the sameSite cookie 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: response.addCookie(cookie);
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application

(cookie-missing-samesite-java)


[warning] 58-58: A cookie was detected without setting the 'HttpOnly' flag. The 'HttpOnly' flag for cookies instructs the browser to forbid client-side scripts from reading the cookie. Set the 'HttpOnly' flag by calling 'cookie.setHttpOnly(true);
Context: response.addCookie(cookie);
Note: [CWE-1004] Sensitive Cookie Without 'HttpOnly' Flag. [REFERENCES]
- https://owasp.org/www-community/HttpOnly

(cookie-missing-httponly-java)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
src/main/java/konkuk/thip/config/SecurityConfig.java (1)

27-28: 상수 직접 문자열 의존 제거 OK

JWT_HEADER_KEY 상수 사용으로 일관성과 오타 방지 측면에서 개선되었습니다.

src/main/java/konkuk/thip/user/application/port/in/UserSignupUseCase.java (1)

8-8: 반환형 변경 적용 현황 점검 및 테스트·문서 검토 요청

UserSignupUseCase 반환형을 Long에서 UserSignupResult로 변경한 사항이 아래 위치에 모두 반영되었습니다:

  • 서비스 구현
    • src/main/java/konkuk/thip/user/application/service/UserSignupService.java (implements UserSignupUseCase, return UserSignupResult.of(...))
  • 웹 어댑터(컨트롤러)
    • src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (UserSignupResponse.of(userSignupUseCase.signup(...)))
  • 포트용 DTO
    • src/main/java/konkuk/thip/user/application/port/in/dto/UserSignupResult.java
  • 응답 매핑용 레코드
    • src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java

기존 Long 반환을 기대하던 로직은 더 이상 코드베이스에서 발견되지 않습니다.
테스트 코드(src/test/java)에서 오래된 반환형을 참조하는 부분이 없는지, Javadoc/README 등 문서에 변경된 계약 사항이 명시됐는지 추가 검토 부탁드립니다.

src/main/java/konkuk/thip/user/adapter/in/web/UserCommandController.java (1)

41-43: 컨트롤러에서 토큰 생성 제거: 책임 분리 👍

토큰 생성 책임을 서비스로 이동시켜 컨트롤러를 얇게 유지한 점이 좋습니다. 응답 일관성도 개선되었습니다.

src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java (3)

32-36: onAuthenticationSuccess 시그니처/흐름 변경 LGTM

메서드 포맷팅과 분기 로직 구조는 명확합니다. 이후 필터 레벨에서 쿠키에서 토큰을 읽어 인증 컨텍스트를 재구성하는지(헤더 기반만 지원하지 않는지)만 확인 부탁드립니다.


4-4: 임포트/정적 임포트 변경 사항 문제 없음

변경된 임포트/정적 임포트는 현재 코드 흐름과 일치합니다.

Also applies to: 17-17


54-60: CSRF 보호 설정: 비활성화 발견됨
Cookie 기반 인증 사용 시 CSRF 방어가 필수입니다. 현재 SecurityConfig에서 .csrf(AbstractHttpConfigurer::disable) 로 CSRF가 완전히 비활성화되어 있습니다.

확인 위치:

  • 파일: src/main/java/konkuk/thip/config/SecurityConfig.java
  • 라인: 62 (.csrf(AbstractHttpConfigurer::disable))

제안하는 수정 예시:

-         .csrf(AbstractHttpConfigurer::disable)
+         .csrf(csrf -> csrf
+             .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
+         )
  • CookieCsrfTokenRepository를 임포트하세요:
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
  • 프론트엔드가 발급된 CSRF 토큰을 요청 헤더 또는 쿠키로 전달하도록 구현해야 합니다.

Likely an incorrect or invalid review comment.

Comment thread build.gradle
Comment on lines +78 to 80
// Spring Boot Actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Actuator 도입 시 엔드포인트 노출/보호 설정 확인 필요

현재 SecurityConfig에서 /actuator/health만 permitAll 처리되어 있어 기본적으로는 안전합니다. 다만 운영/스테이징 전역 설정에서 노출 범위를 명시적으로 health만 포함하도록 고정해 주세요.

다음 스크립트로 설정 여부를 점검할 수 있습니다:

권장 설정 예시(운영/스테이징):

management:
  endpoints:
    web:
      base-path: /actuator
      exposure:
        include: "health"
  endpoint:
    health:
      probes:
        enabled: true
      show-details: "never"

🏁 Script executed:

#!/bin/bash
# Actuator 노출 설정 점검
echo "== application.*(yml|properties)에서 management 설정 검색 =="
fd -a -t f 'application.*\.(ya?ml|properties)$' | xargs -I{} sh -c 'echo "--- {} ---"; rg -n "management\." "{}" || true'

echo
echo "== 코드에서 actuator 경로 화이트리스트 점검 =="
rg -n "/actuator" -S src/main/java || true

Length of output: 475


Actuator 노출 설정: 운영/스테이징 환경에 명시적 관리 엔드포인트 노출 범위 추가 필요

설정 파일(application-*.yml.properties)에서 management.endpoints.web.exposure.include가 누락되어 있어, 기본값(health, info) 중 info가 의도치 않게 노출될 수 있습니다. SecurityConfig에서는 이미 /actuator/healthpermitAll 처리(코드: src/main/java/konkuk/thip/config/SecurityConfig.java:52)되어 있으나, 운영·스테이징 전역 설정에서도 오직 health만 노출하도록 아래 내용을 반영해 주세요.

• 대상 파일

  • src/main/resources/application-production.yml
  • src/main/resources/application-staging.yml

• 권장 설정 예시

management:
  endpoints:
    web:
      base-path: /actuator         # 기본값과 동일하지만 명시 권장
      exposure:
        include: health            # health만 노출
  endpoint:
    health:
      show-details: never          # 상세 정보 숨기기
      probes:
        enabled: true              # 상태 프로브 사용
🤖 Prompt for AI Agents
In src/main/resources/application-production.yml and application-staging.yml,
add explicit actuator exposure settings to restrict exposed endpoints to only
health. Include the management.endpoints.web.base-path set to /actuator,
management.endpoints.web.exposure.include set to health, and configure
management.endpoint.health to hide details and enable probes as shown in the
recommended YAML snippet. This ensures only the health endpoint is exposed in
production and staging environments.

Comment thread src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java Outdated
Comment on lines +29 to +43
public BaseResponse<OAuth2TokenResponse> checkUserExists(
@Parameter(description = "소셜 로그인 ID (형식: {provider}_{식별자 ID})", example = "kakao_1234567890")
@RequestParam("oauth2Id") String oauth2Id,
HttpServletResponse response) throws IOException {
return userJpaRepository.findByOauth2Id(oauth2Id)
.map(user -> {
// 기존 유저: AccessToken 발급
String accessToken = jwtUtil.createAccessToken(user.getUserId());
return BaseResponse.ok(OAuth2TokenResponse.of(accessToken,false));
})
.orElseGet(() -> {
// 신규 유저: SignupToken 발급
String tempToken = jwtUtil.createSignupToken(oauth2Id);
return BaseResponse.ok(OAuth2TokenResponse.of(tempToken, true));
});

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

크리티컬: 소셜 공급자 토큰 검증 없이 oauth2Id만으로 액세스/가입 토큰 발급

현재 구현은 임의의 oauth2Id를 넘기면:

  • 기존 유저: 해당 유저의 AccessToken 발급
  • 미존재 유저: SignupToken 발급

즉, 공격자가 타인의 oauth2Id를 알고 있으면 그 계정의 AccessToken을 획득할 수 있습니다. 이는 임퍼소네이션 취약점입니다. 아래를 최소 요건으로 권장합니다.

필수 조치:

  • 클라이언트가 공급자 액세스 토큰(또는 ID 토큰)을 제출하도록 요구
  • 서버에서 공급자 API로 토큰 유효성 검증 및 사용자 식별자 조회
  • 공급자에서 확인된 식별자와 클라이언트가 보낸 oauth2Id(또는 서버 파생값)를 대조하여 불일치 시 401/403 반환
  • 유저 존재 여부 판단은 “검증된 식별자”만 사용

추가 보강:

  • 엔드포인트를 POST로 전환하고 바디(JSON)로 입력 받기(쿼리스트링/로그 노출 방지)
  • 레이트 리밋 및 감사 로깅(토큰/식별자 값 자체는 마스킹)
  • SignupToken은 짧은 TTL/스코프 제한(회원가입 전용)

원하는 경우, 카카오/네이버/구글 각각의 검증 서비스 스켈레톤 코드를 제공하겠습니다.

🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java
around lines 29 to 43, the current method issues access or signup tokens based
solely on the provided oauth2Id without verifying the social provider's token,
causing a critical impersonation vulnerability. To fix this, change the endpoint
to accept a POST request with a JSON body containing both the oauth2Id and the
provider's access or ID token. Implement server-side verification of the
provider token by calling the provider's API to confirm its validity and
retrieve the user identifier. Compare the verified identifier with the provided
oauth2Id and return a 401 or 403 error if they do not match. Only after
successful verification should you check user existence and issue tokens.
Additionally, consider adding rate limiting, audit logging with masking, and
ensure signup tokens have short TTL and limited scope.

Comment on lines +26 to +27
@Value("${server.web-redirect-url}")
private String webRedirectUrl;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

webRedirectUrl 프로퍼티 존재/값 검증

외부 설정값에 의존하므로 프로퍼티 누락/오타 시 런타임 실패합니다. 또한 REDIRECT_*와의 문자열 결합 시 슬래시 중복/누락 여부를 확인하세요.

다음 스크립트로 프로퍼티 정의와 REDIRECT_* 상수 값을 확인해 주세요(선행/후행 슬래시 일관성):


🏁 Script executed:

#!/bin/bash
# server.web-redirect-url 정의 확인
rg -n --pretty $'server.web-redirect-url'

# REDIRECT_* 상수 정의 확인
rg -n --pretty $'REDIRECT_SIGNUP_URL|REDIRECT_HOME_URL' src/main/java

Length of output: 671


server.web-redirect-url 프로퍼티 추가 및 슬래시 일관성 검증 필요
application.yml(또는 application.properties)에 server.web-redirect-url 설정이 정의되어 있지 않습니다. 런타임 에러 방지를 위해 설정 파일에 해당 프로퍼티를 추가해 주세요.
또한, AuthParameters에 선언된 상수

  • REDIRECT_SIGNUP_URL("/signup/genre")
  • REDIRECT_HOME_URL("/feed")
    와 결합할 때
  • webRedirectUrl 값이 끝에 /를 포함하는지
  • 결합 시 앞뒤로 슬래시가 중복(//)되거나 누락되지 않는지

아래 지점을 확인 및 수정해 주시기 바랍니다.

• src/main/resources/application.yml (또는 application.properties)에 server.web-redirect-url 추가
• src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java
webRedirectUrl + REDIRECT_* 조합 시 URL 포맷 일관성 검증

🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java
around lines 26 to 27, ensure the property server.web-redirect-url is defined in
application.yml or application.properties to prevent runtime errors.
Additionally, when concatenating webRedirectUrl with AuthParameters constants
like REDIRECT_SIGNUP_URL and REDIRECT_HOME_URL, verify and normalize the URL
format to avoid duplicate or missing slashes by trimming trailing slashes from
webRedirectUrl or leading slashes from the constants as needed for consistent
URL construction.

Comment on lines +41 to 51
if (oAuth2User.isNewUser()) {
// 신규 유저 - 회원가입용 임시 토큰
String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id());
response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + tempToken);
writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser()));
return;
addTokenCookie(response, tempToken);
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_SIGNUP_URL);
} else {
// 기존 유저 - 로그인용 액세스 토큰
String accessToken = jwtUtil.createAccessToken(loginUser.userId());
addTokenCookie(response, accessToken);
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_HOME_URL);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

회원가입 토큰과 액세스 토큰의 쿠키 이름 분리 권장

현재 동일한 쿠키 이름(JWT_HEADER_KEY)을 사용하여 회원가입용 토큰/액세스 토큰을 모두 저장합니다. 서버 측 로직이 클레임 키로 구분하더라도, 엔드포인트의 기대치(액세스 토큰 필요)와 실제 전달값(회원가입 토큰)이 엇갈리면 예기치 않은 실패/혼동이 발생할 수 있습니다. 쿠키 키를 분리해 의도를 명확히 하세요.

호출부와 메서드 시그니처를 아래처럼 수정하는 것을 제안합니다:

-            String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id());
-            addTokenCookie(response, tempToken);
+            String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id());
+            addTokenCookie(response, tempToken, "signup_token");
             getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_SIGNUP_URL);
...
-            String accessToken = jwtUtil.createAccessToken(loginUser.userId());
-            addTokenCookie(response, accessToken);
+            String accessToken = jwtUtil.createAccessToken(loginUser.userId());
+            addTokenCookie(response, accessToken, "access_token");
             getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_HOME_URL);

그리고 addTokenCookie 시그니처/구현도 함께 변경합니다(아래 라인 54-60에 적용):

-    private void addTokenCookie(HttpServletResponse response, String token) {
+    private void addTokenCookie(HttpServletResponse response, String token, String cookieName) {
-        ResponseCookie cookie = ResponseCookie.from(JWT_HEADER_KEY.getValue(), token)
+        ResponseCookie cookie = ResponseCookie.from(cookieName, token)
             ...
     }

참고: 회원가입 완료 시 signup_token 쿠키를 즉시 삭제(또는 access_token으로 교체)하는 정리 로직도 추가하세요.


회원가입 토큰 vs 액세스 토큰 쿠키 키 분리 필요

현재 CustomSuccessHandler에서 신규 가입용 토큰(signup)과 로그인용 액세스 토큰(access)을 동일한 쿠키 키(JWT_HEADER_KEY)로 저장하고 있어, 잘못된 쿠키가 전달될 경우 엔드포인트의 기대값과 실제 토큰이 달라 예기치 않은 실패가 발생할 수 있습니다. 별도 쿠키 이름으로 명확히 구분하세요.

수정 대상:

  • 파일: src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java

제안된 수정 예시:

@@ -41,7 +41,7 @@
         if (oAuth2User.isNewUser()) {
             // 신규 유저 - 회원가입용 임시 토큰
-            addTokenCookie(response, tempToken);
+            addTokenCookie(response, tempToken, "signup_token");
             getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_SIGNUP_URL);
         } else {
             // 기존 유저 - 로그인용 액세스 토큰
@@ -45,7 +45,7 @@
-            addTokenCookie(response, accessToken);
+            addTokenCookie(response, accessToken, "access_token");
             getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_HOME_URL);
@@ -54,7 +54,7 @@
-    private void addTokenCookie(HttpServletResponse response, String token) {
+    private void addTokenCookie(HttpServletResponse response, String token, String cookieName) {
         ResponseCookie cookie = ResponseCookie.from(cookieName, token)
             .httpOnly(true)
             .secure(isProd)

추가로, 회원가입 완료 후에는 signup_token 쿠키를 즉시 삭제하거나 access_token 쿠키로 교체하는 정리 로직도 구현해 주세요.

📝 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
if (oAuth2User.isNewUser()) {
// 신규 유저 - 회원가입용 임시 토큰
String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id());
response.setHeader(JWT_HEADER_KEY.getValue(), JWT_PREFIX.getValue() + tempToken);
writeResponse(response, BaseResponse.ok(oAuth2User.getLoginUser()));
return;
addTokenCookie(response, tempToken);
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_SIGNUP_URL);
} else {
// 기존 유저 - 로그인용 액세스 토큰
String accessToken = jwtUtil.createAccessToken(loginUser.userId());
addTokenCookie(response, accessToken);
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_HOME_URL);
}
if (oAuth2User.isNewUser()) {
// 신규 유저 - 회원가입용 임시 토큰
String tempToken = jwtUtil.createSignupToken(loginUser.oauth2Id());
addTokenCookie(response, tempToken, "signup_token");
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_SIGNUP_URL);
} else {
// 기존 유저 - 로그인용 액세스 토큰
String accessToken = jwtUtil.createAccessToken(loginUser.userId());
addTokenCookie(response, accessToken, "access_token");
getRedirectStrategy().sendRedirect(request, response, webRedirectUrl + REDIRECT_HOME_URL);
}
// …
// 기존: private void addTokenCookie(HttpServletResponse response, String token) {
private void addTokenCookie(HttpServletResponse response, String token, String cookieName) {
ResponseCookie cookie = ResponseCookie.from(cookieName, token)
.httpOnly(true)
.secure(isProd)
.path("/")
.maxAge(expires)
.build();
response.addHeader("Set-Cookie", cookie.toString());
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java
around lines 41 to 51, the signup token and access token are stored using the
same cookie key, which can cause token confusion. Fix this by defining and using
separate cookie keys for the signup token and the access token when calling
addTokenCookie. Additionally, implement logic to clear the signup token cookie
or replace it with the access token cookie immediately after signup completion
to avoid stale tokens.

Comment on lines +54 to 60
private void addTokenCookie(HttpServletResponse response, String token) {
Cookie cookie = new Cookie(JWT_HEADER_KEY.getValue(), token);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(COOKIE_MAX_AGE);
response.addCookie(cookie);
}

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

⚠️ Potential issue

쿠키 보안 플래그 보강 필요(HttpOnly, SameSite 미설정) 및 CSRF 노출 가능성

현재 토큰 쿠키에 Secure/Path/Max-Age만 설정되어 있고 HttpOnly와 SameSite가 설정되지 않았습니다. CSRF 관점에서 쿠키 기반 인증을 사용하면 공격면이 넓어집니다. 최소한 HttpOnly를 추가하고, SameSite 정책을 명시적으로 설정하세요. 크로스사이트 요청이 필요 없다면 Lax/Strict, 필요하다면 None; Secure(+ CSRF 토큰)로 가는 것을 권장합니다.

아래와 같이 Spring의 ResponseCookie를 사용하면 SameSite를 안전하게 설정할 수 있습니다.

-    private void addTokenCookie(HttpServletResponse response, String token) {
-        Cookie cookie = new Cookie(JWT_HEADER_KEY.getValue(), token);
-        cookie.setSecure(true);
-        cookie.setPath("/");
-        cookie.setMaxAge(COOKIE_MAX_AGE);
-        response.addCookie(cookie);
-    }
+    private void addTokenCookie(HttpServletResponse response, String token) {
+        // SameSite 값은 서비스 특성에 맞게 "Lax" 또는 "None" 중 선택하세요.
+        ResponseCookie cookie = ResponseCookie.from(JWT_HEADER_KEY.getValue(), token)
+                .httpOnly(true)
+                .secure(true)
+                .path("/")
+                .maxAge(COOKIE_MAX_AGE) // Duration 사용 제안 (아래 별도 코멘트/디프 참고)
+                .sameSite("Lax")
+                .build();
+        response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
+    }

추가 import가 필요합니다(파일 상단에 추가):

import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import java.time.Duration;

또한, 쿠키 인증을 계속 사용할 경우 CSRF 방어 전략을 반드시 병행하세요(예: CookieCsrfTokenRepository, Double Submit Cookie 패턴 등). SameSite=None을 택하면 CSRF 방어가 더 중요해집니다.

🧰 Tools
🪛 ast-grep (0.38.6)

[warning] 58-58: 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 Token or Double Submit Cookie patterns with defense-in-depth mechanisms such as the sameSite cookie 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: response.addCookie(cookie);
Note: [CWE-352] Cross-Site Request Forgery (CSRF). [REFERENCES]
- https://stackoverflow.com/questions/42717210/samesite-cookie-in-java-application

(cookie-missing-samesite-java)


[warning] 58-58: A cookie was detected without setting the 'HttpOnly' flag. The 'HttpOnly' flag for cookies instructs the browser to forbid client-side scripts from reading the cookie. Set the 'HttpOnly' flag by calling 'cookie.setHttpOnly(true);
Context: response.addCookie(cookie);
Note: [CWE-1004] Sensitive Cookie Without 'HttpOnly' Flag. [REFERENCES]
- https://owasp.org/www-community/HttpOnly

(cookie-missing-httponly-java)

🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/common/security/oauth2/CustomSuccessHandler.java
around lines 54 to 60, the addTokenCookie method sets only Secure, Path, and
Max-Age on the cookie but lacks HttpOnly and SameSite attributes, increasing
CSRF risk. Replace the Cookie creation with Spring's ResponseCookie builder to
set HttpOnly(true) and specify SameSite policy (Lax or Strict if cross-site
requests are not needed, or None with Secure if they are). Add necessary imports
for ResponseCookie, HttpHeaders, and Duration at the top of the file. Also,
ensure CSRF protection strategies are implemented alongside cookie
authentication.

Comment thread src/main/java/konkuk/thip/config/SecurityConfig.java
Comment on lines +5 to 11
public record UserSignupResponse(
Long userId,
String accessToken
) {
public static UserSignupResponse of(UserSignupResult userSignupResult) {
return new UserSignupResponse(userSignupResult.userId(), userSignupResult.accessToken());
}

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

토큰 포함 응답의 캐시 방지 및 로깅 주의

이 응답에는 액세스 토큰이 포함됩니다. 중간 프록시/브라우저 캐시를 막기 위해 전역적으로 아래 보안 헤더 설정을 권장합니다.

  • Cache-Control: no-store, Pragma: no-cache

또한 응답 바디 로깅이 활성화되어 있다면 토큰 마스킹을 적용해 주세요.

추가 예시(전역 Spring Security 설정):

http.headers(headers -> headers.cacheControl(cache -> {})); // no-store 기본 적용
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/in/web/response/UserSignupResponse.java
around lines 5 to 11, the response includes an access token which should not be
cached by browsers or proxies. To fix this, configure global security headers in
your Spring Security setup to include Cache-Control: no-store and Pragma:
no-cache to prevent caching. Additionally, if response body logging is enabled,
implement token masking to avoid exposing sensitive token data in logs.

@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: 0

🧹 Nitpick comments (3)
src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java (1)

79-91: shouldNotFilter 범위를 더 안전하게: 정확 경로 + POST 메서드로 제한 제안

현재 startsWith("/oauth2/users")는 /oauth2/users뿐 아니라 하위 경로 전부를 제외합니다. 의도된 엔드포인트가 POST /oauth2/users 하나라면:

  • 정확 경로 매칭("/oauth2/users".equals(servletPath))
  • HTTP 메서드 검증(POST만 허용)
    을 함께 적용해 공격면을 줄이는 것을 권장합니다.

예시(diff):

-    protected boolean shouldNotFilter(HttpServletRequest request) {
-        String path = request.getRequestURI();
+    protected boolean shouldNotFilter(HttpServletRequest request) {
+        String path = request.getServletPath(); // 컨텍스트 경로 영향 배제
+        boolean oauth2Users = "/oauth2/users".equals(path)
+                && "POST".equalsIgnoreCase(request.getMethod());
         // 화이트리스트 경로에 대해서는 JWT 필터 제외
-        return path.startsWith("/swagger-ui")
+        return path.startsWith("/swagger-ui")
                 || path.startsWith("/v3/api-docs")
                 || path.startsWith("/api-docs")
                 || path.startsWith("/oauth2/authorization")
                 || path.startsWith("/login/oauth2/code")
-                || path.startsWith("/oauth2/users")
+                || oauth2Users
                 ;
     }
src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenResponse.java (2)

3-10: 토큰 의미 명확화 제안: tokenType(ACCESS|SIGNUP) 및 expiresIn 추가

현재 token 필드가 액세스 토큰/가입 임시 토큰을 상황에 따라 담아 혼동 여지가 있습니다. 클라이언트 분기 로직을 명확히 하기 위해 tokenType(ACCESS|SIGNUP)과 만료 정보(expiresIn)를 함께 제공하는 것을 권장합니다.

예시(diff):

-public record OAuth2TokenResponse(
-        String token,
-        boolean isNewUser
-) {
-    public static OAuth2TokenResponse of(String token, boolean isNewUser) {
-        return new OAuth2TokenResponse(token, isNewUser);
-    }
-}
+public record OAuth2TokenResponse(
+        String token,
+        TokenType tokenType, // ACCESS or SIGNUP
+        boolean isNewUser,
+        long expiresIn // seconds
+) {
+    public static OAuth2TokenResponse of(String token, TokenType tokenType, boolean isNewUser, long expiresIn) {
+        return new OAuth2TokenResponse(token, tokenType, isNewUser, expiresIn);
+    }
+}

추가 파일(예시):

// src/main/java/konkuk/thip/common/security/oauth2/dto/TokenType.java
package konkuk.thip.common.security.oauth2.dto;

public enum TokenType {
    ACCESS, SIGNUP
}

추가로 공개 API 문서화를 위해 @Schema(description=...) 등을 고려하면 사용처 이해가 쉬워집니다.


3-10: 불린 필드 명명 nit: isNewUser → newUser 고려

record 컴포넌트가 isNewUser이면 접근자가 isNewUser()로 생성되어 JSON도 보통 "isNewUser"가 됩니다. 선호에 따라 "newUser"를 쓰면 더 자연스러운 경우가 많습니다. 기존과의 호환이 필요 없다면 다음처럼 단순화할 수 있습니다.

-public record OAuth2TokenResponse(
-        String token,
-        boolean isNewUser
-) {
-    public static OAuth2TokenResponse of(String token, boolean isNewUser) {
-        return new OAuth2TokenResponse(token, isNewUser);
-    }
-}
+public record OAuth2TokenResponse(
+        String token,
+        boolean newUser
+) {
+    public static OAuth2TokenResponse of(String token, boolean newUser) {
+        return new OAuth2TokenResponse(token, newUser);
+    }
+}

만약 응답 JSON 키를 "isNewUser"로 유지하고 싶다면, 다음과 같이 컴포넌트에 @JsonProperty("isNewUser")를 적용하는 방식도 있습니다.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 85c9800 and 0938f87.

📒 Files selected for processing (4)
  • src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenRequest.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenResponse.java (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenRequest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/java/konkuk/thip/common/security/filter/JwtAuthenticationFilter.java (2)

79-91: 화이트리스트에 /oauth2/users 추가 — 의도에 부합하며 적절합니다

소셜 로그인 사용자인증 사전확인을 위한 비인증 접근이 요구되므로 필터 제외 대상에 추가한 결정은 타당합니다.


79-91: JWT 필터 제외 경로(/oauth2/users) 일치 확인 완료

  • src/main/java/konkuk/thip/config/SecurityConfig.java
    • WHITELIST 배열에 "/oauth2/users"가 포함되어 있으며
    • .authorizeHttpRequests(auth -> auth.requestMatchers(WHITELIST).permitAll()) 처리됨
  • src/main/java/konkuk/thip/common/security/oauth2/CustomOAuth2UserController.java
    • @PostMapping("/oauth2/users")로 POST 엔드포인트가 정의되어 있음

따라서 JWT 필터의 shouldNotFilter 예외 설정과 보안 설정 및 컨트롤러 매핑이 모두 일치하며, 추가 조치는 필요 없습니다.

src/main/java/konkuk/thip/common/security/oauth2/dto/OAuth2TokenResponse.java (1)

3-10: 단순하고 명확한 OAuth2 응답 DTO 도입 — LGTM

레코드 기반 DTO로 불변성과 직렬화 용이성을 확보했고, 신규 사용자 여부 신호도 함께 전달합니다. 목적에 부합합니다.

@hd0rable hd0rable left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

수고하셨습니닷 간단한 리뷰 남겼는데 확인 부탁드립낟ㅅ

public BaseResponse<OAuth2TokenResponse> checkUserExists(
@Parameter(description = "소셜 로그인 ID (형식: {provider}_{식별자 ID})", example = "kakao_1234567890")
@RequestBody OAuth2TokenRequest oAuth2TokenRequest) throws IOException {
return userJpaRepository.findByOauth2Id(oAuth2TokenRequest.oauth2Id())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

오 컨트롤러에서 처리하시는군요 좋습니다

ObjectMapper objectMapper = new ObjectMapper();
String body = objectMapper.writeValueAsString(value);
response.getWriter().write(body);
private void addTokenCookie(HttpServletResponse response, String token) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

p3: 엇 웹은 쿠키로 토큰 전달하는건가요?? 헤더에 넣어주고 httpOnly 속성 해제하는 걸로 통일하는거 아니였나요? 쿠키로 전달할거면 httpOnly속성을 추가하는게 좋아보입니닷!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

웹에서는 서버 측에서 직접 리다이렉트를 처리해야 하기 때문에, 리다이렉트 시 토큰을 헤더에 담아 전달할 수 없고 쿠키 방식을 사용할 수밖에 없습니다.

또한 앞서 이야기한 것처럼 HttpOnly 옵션을 설정하면 JS에서 쿠키에 접근해 토큰을 추출할 수 없기 때문에, 웹은 쿠키 기반 인가, 안드는 헤더 기반 인가로 두 가지 방식으로 나뉘게 됩니다.

하지만 저희 서비스는 민감한 개인정보를 다루는 수준은 아니라고 판단하여, HttpOnly 옵션을 해제하고 JS에서 쿠키에 접근해 토큰을 추출한 뒤, 이후 API 요청부터는 헤더에 토큰을 담아 보내는 방식으로 안드로이드와 웹의 인가 방식을 통일하기로 결정하였습니다!!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

아하 그 후의 인증방식이 통일이고 인가방법이 분기 처리였군여 제가 착각했습니닿 마루머쓱

config.setMaxAge(3600L);

config.setExposedHeaders(Collections.singletonList("Authorization"));
config.setExposedHeaders(Collections.singletonList(JWT_HEADER_KEY.getValue()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

오 enum 좋습니다

@seongjunnoh seongjunnoh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM 바뀐 소셜 로그인 로직 확인했습니다 머지하겠습니다!!

Comment on lines +3 to +6
public record OAuth2TokenResponse(
String token,
boolean isNewUser
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

굳굳

Comment on lines +6 to +7
Long userId,
String accessToken

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

확인햇습니다

@seongjunnoh seongjunnoh merged commit 7b20802 into develop Aug 8, 2025
2 checks passed
@seongjunnoh seongjunnoh deleted the feat/#174-check-user-persist branch August 8, 2025 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[THIP2025-227] [feat] 신규 사용자 여부 확인

3 participants