Skip to content

[FEAT] 소셜 로그인 API및 Security, JWT#41

Merged
kimyw1018 merged 38 commits into
developfrom
feat/#25-social-login-jwt
Jul 24, 2026
Merged

[FEAT] 소셜 로그인 API및 Security, JWT#41
kimyw1018 merged 38 commits into
developfrom
feat/#25-social-login-jwt

Conversation

@kimyw1018

@kimyw1018 kimyw1018 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📍 개요

Spring Security 기반 인증/인가 체인 구축, JWT 토큰 발급/검증 시스템 구현 및 소셜 로그인 API 뼈대 작성

⛓️‍💥 관련 이슈


🛠️ 작업 내용

  • Security & JWT 보안 인프라 구축
  • 소셜 로그인 도메인 API 뼈대 구현

🔥 리뷰 요청 사항

리뷰어가 중점적으로 확인해주었으면 하는 내용을 작성해주세요.

  • SecurityUtil.getCurrentUserId() 유틸 동작 방식
  • com.mr.global.security 하위에 jwt 및 principal로 역할 분리

✅ 체크리스트

  • 코드 컨벤션을 준수했습니다.
  • 불필요한 코드 및 import를 제거했습니다.
  • 예외 처리를 적용했습니다.
  • 테스트를 완료했습니다.
  • 관련 Issue를 연결했습니다.

📎 참고 사항

  • 소셜 로그인 완성은 다음에 올릴 pr에서 할 부분이라 시큐리티랑 인프라 쪽만 봐주세요!!

Summary by CodeRabbit

  • 새 기능
    • 소셜 로그인 API 추가: /api/auth/login/{socialType}에서 액세스/리프레시 토큰과 사용자 정보(신규 여부 포함) 반환
    • 토큰 재발급 요청 DTO 제공
  • 보안
    • JWT(무상태) 인증으로 보호 API 접근 강화, 401/403 공통 JSON 응답 제공
    • CORS 설정 추가(로컬 및 *.musereview.site 허용), CSRF 비활성화
  • 문서
    • 로컬 설정 예시 application.example.yml 추가/보강

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kimyw1018, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 04352e60-96b5-4b4c-91fe-d0550b6c404d

📥 Commits

Reviewing files that changed from the base of the PR and between 4530b88 and 1b90a4d.

📒 Files selected for processing (1)
  • src/main/java/com/mr/global/security/SecurityConfig.java
📝 Walkthrough

Walkthrough

소셜 로그인 API와 인증 DTO를 추가하고, mock 사용자 기반 JWT 발급 및 Bearer 토큰 인증 흐름을 구성했습니다. 무상태 보안 설정, CORS, 401·403 응답, 사용자 ID 조회 유틸리티와 JWT 설정 예시도 포함됩니다.

Changes

인증 흐름

Layer / File(s) Summary
JWT 토큰 및 사용자 주체 기반
src/main/java/com/mr/global/security/jwt/*, src/main/java/com/mr/global/security/principal/*, src/main/java/com/mr/domain/user/entity/enums/UserRole.java
JWT 설정·생성·검증과 UserDetails 기반 인증 주체 및 사용자 조회 서비스를 추가합니다.
소셜 로그인 API와 응답 계약
src/main/java/com/mr/domain/auth/dto/*, src/main/java/com/mr/domain/auth/controller/AuthController.java, src/main/java/com/mr/domain/auth/service/AuthService.java, src/main/java/com/mr/domain/auth/entity/enums/SocialType.java, src/main/java/com/mr/domain/auth/entity/SocialAuth.java
검증된 소셜 로그인 요청을 받아 mock 사용자 정보로 JWT 로그인 응답을 반환합니다.
JWT 보안 필터 체인
src/main/java/com/mr/global/security/SecurityConfig.java, src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java, src/main/java/com/mr/global/config/SwaggerConfig.java, src/main/resources/application.example.yml, MR_config/local/application.example.yml
CORS, 무상태 세션, 공개 경로, Bearer JWT 필터와 관련 설정 예시를 구성합니다.
보안 실패 응답과 현재 사용자 식별
src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java, src/main/java/com/mr/global/security/jwt/JwtAccessDeniedHandler.java, src/main/java/com/mr/global/security/SecurityUtil.java, src/main/java/com/mr/global/apipayload/code/CommonStatus.java
인증 실패·권한 거부 JSON 응답과 현재 인증 사용자 ID 조회를 추가합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AuthController
  participant AuthService
  participant JwtTokenProvider
  participant JwtAuthenticationFilter
  participant SecurityContextHolder
  Client->>AuthController: 소셜 로그인 요청
  AuthController->>AuthService: socialLogin(socialType, accessToken)
  AuthService->>JwtTokenProvider: access/refresh JWT 생성
  JwtTokenProvider-->>AuthService: JWT 반환
  AuthService-->>Client: ApiResponse<LoginResponse>
  Client->>JwtAuthenticationFilter: Bearer JWT 요청
  JwtAuthenticationFilter->>JwtTokenProvider: access token 검증 및 인증 조회
  JwtTokenProvider-->>SecurityContextHolder: Authentication 설정
Loading

Possibly related PRs

  • Musereview/BE#2: 공통 오류 코드와 기존 보안 설정을 현재 JWT 보안 구조로 확장·대체합니다.
  • Musereview/BE#14: SocialType enum 식별자 변경을 직접 공유합니다.

Poem

소셜 토큰 문을 열고
JWT 두 마리 날아가네
필터는 조용히 검증하고
인증 주체 자리 잡네
401과 403도 JSON으로 찰칵!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 소셜 로그인 API, Security, JWT 변경을 직접 담아 PR의 핵심을 잘 요약합니다.
Linked Issues check ✅ Passed 이슈 #25의 카카오/구글 소셜 로그인과 JWT 인증 목표에 맞춰 관련 인프라와 API 뼈대를 추가했습니다.
Out of Scope Changes check ✅ Passed 인증 인프라, principal, 설정 파일 중심 변경이라 목적과 무관한 별도 기능 추가는 보이지 않습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#25-social-login-jwt

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.

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

🧹 Nitpick comments (6)
src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

직접 생성한 ObjectMapper 대신 Spring이 관리하는 Bean을 주입하세요.

new ObjectMapper()는 프로젝트 전역에서 설정된 Jackson 모듈(예: JavaTimeModule, 네이밍 전략 등)을 반영하지 않아, 다른 컨트롤러 응답과 직렬화 형식이 달라질 수 있습니다. 생성자 주입으로 교체하는 것을 권장합니다.

+import lombok.RequiredArgsConstructor;
+
 `@Component`
+@RequiredArgsConstructor
 public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

-    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final ObjectMapper objectMapper;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java` at
line 18, Replace the directly instantiated ObjectMapper field in
JwtAuthenticationEntryPoint with constructor injection of the Spring-managed
ObjectMapper bean, removing the new ObjectMapper() initialization so shared
Jackson modules and serialization settings are used.
src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java (1)

38-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

매직 넘버 대신 BEARER_PREFIX.length()를 사용하세요.

지금은 "Bearer "가 7글자라 우연히 맞지만, 상수 값이 바뀌면 잘라내는 위치가 틀어집니다.

-            return bearerToken.substring(7);
+            return bearerToken.substring(BEARER_PREFIX.length());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java` around
lines 38 - 44, Update resolveToken to derive the substring start index from
BEARER_PREFIX.length() instead of the hardcoded 7, while preserving the existing
authorization-header validation and token extraction behavior.
src/main/java/com/mr/global/config/SwaggerConfig.java (1)

1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

빈 클래스, 현재는 아무 역할도 하지 않습니다.

@Configuration 애노테이션도 없고 OpenAPI Bean 정의도 없어 Spring이 이 클래스를 아예 로드하지 않습니다. 이번 PR이 소셜 로그인 인프라 뼈대 구축 단계라 후속 커밋에서 채워질 스텁으로 보이지만, 그렇다면 TODO 주석을 남겨두면 의도를 명확히 전달할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/config/SwaggerConfig.java` around lines 1 - 4,
SwaggerConfig 빈 클래스에 후속 구현 예정임을 나타내는 TODO 주석을 추가하세요. 현재 클래스 구조와 패키지는 유지하고,
OpenAPI Bean이나 `@Configuration` 애노테이션 등 범위를 벗어난 변경은 하지 마세요.
src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java (2)

27-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

필드명과 실제 저장값이 어긋나 있습니다.

accessTokenValidityInMilliseconds/refreshTokenValidityInMilliseconds는 실제로는 jwt.*-validity-in-seconds 프로퍼티(초 단위) 값을 그대로 담고, ms 변환은 사용 시점(Line 44, 57)에서만 일어납니다. 네이밍을 ...InSeconds로 바꾸는 편이 명확합니다.

덧붙여, AuthService(연관 레이어)에서 tokenExpirationTime3600L로 하드코딩하고 있는데, 이 필드에 대한 getter를 제공하면 실제 설정값과 응답이 항상 일치하게 만들 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java` around lines
27 - 31, Rename the JwtTokenProvider fields accessTokenValidityInMilliseconds
and refreshTokenValidityInMilliseconds to accessTokenValidityInSeconds and
refreshTokenValidityInSeconds, updating all references while preserving the
existing conversion to milliseconds at use sites. Expose a getter for the
configured access-token validity so AuthService can use it instead of hardcoding
tokenExpirationTime as 3600L.

41-65: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Access/Refresh 토큰 생성 로직 중복 제거를 고려해보세요.

두 메서드가 만료시간만 다르고 나머지 로직은 동일합니다. 만료 기간을 파라미터로 받는 private 헬퍼로 통합하면 유지보수성이 좋아집니다.

♻️ 제안 리팩터링
-    public String createAccessToken(Long userId) {
-        Claims claims = Jwts.claims().setSubject(String.valueOf(userId));
-        Date now = new Date();
-        Date validity = new Date(now.getTime() + accessTokenValidityInMilliseconds * 1000);
-
-        return Jwts.builder()
-                .setClaims(claims)
-                .setIssuedAt(now)
-                .setExpiration(validity)
-                .signWith(key, SignatureAlgorithm.HS256)
-                .compact();
-    }
-
-    public String createRefreshToken(Long userId) {
-        Claims claims = Jwts.claims().setSubject(String.valueOf(userId));
-        Date now = new Date();
-        Date validity = new Date(now.getTime() + refreshTokenValidityInMilliseconds * 1000);
-
-        return Jwts.builder()
-                .setClaims(claims)
-                .setIssuedAt(now)
-                .setExpiration(validity)
-                .signWith(key, SignatureAlgorithm.HS256)
-                .compact();
-    }
+    public String createAccessToken(Long userId) {
+        return createToken(userId, accessTokenValidityInMilliseconds);
+    }
+
+    public String createRefreshToken(Long userId) {
+        return createToken(userId, refreshTokenValidityInMilliseconds);
+    }
+
+    private String createToken(Long userId, long validityInSeconds) {
+        Date now = new Date();
+        Date validity = new Date(now.getTime() + validityInSeconds * 1000);
+
+        return Jwts.builder()
+                .setSubject(String.valueOf(userId))
+                .setIssuedAt(now)
+                .setExpiration(validity)
+                .signWith(key, SignatureAlgorithm.HS256)
+                .compact();
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java` around lines
41 - 65, Refactor the duplicated token-building logic in createAccessToken and
createRefreshToken into a private helper that accepts the token validity
duration as a parameter. Keep each public method responsible for supplying its
respective access or refresh validity value, while preserving the existing
claims, timestamps, signing algorithm, and expiration behavior.
src/main/java/com/mr/global/security/SecurityConfig.java (1)

35-39: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

/api/v1/auth/** permitAll 범위, 확장 시 주의가 필요합니다.

현재는 로그인 엔드포인트만 있어 괜찮지만, 나중에 로그아웃·토큰 재발급처럼 인증이 필요한 경로가 /api/v1/auth/** 하위에 추가되면 의도치 않게 무인증 접근이 허용될 수 있습니다. 필요한 경로만 명시적으로 나열하는 방식으로 점진적으로 좁혀가는 것을 권장합니다.

참고로 new JwtAuthenticationFilter(jwtTokenProvider)처럼 직접 생성하는 대신 @Bean으로 등록하면 다른 시큐리티 설정과 일관성을 유지하기 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/global/security/SecurityConfig.java` around lines 35 -
39, In SecurityConfig’s authorizeHttpRequests configuration, replace the broad
/api/v1/auth/** permitAll matcher with explicit matchers for only the currently
public login endpoint, leaving protected authentication-related endpoints
inaccessible by default. Do not broaden the change to JwtAuthenticationFilter
registration unless separately required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/mr/domain/auth/service/AuthService.java`:
- Around line 34-38: Update the token response construction in AuthService to
derive tokenExpirationTime from the same configured access-token validity used
by JwtTokenProvider instead of the hardcoded 3600L. Expose or reuse the
provider’s actual TTL, ensure the response unit is explicitly consistent with
the API contract, and keep the returned expiration synchronized when JWT
settings change.
- Around line 20-32: Update
src/main/java/com/mr/domain/auth/service/AuthService.java:20-32 in socialLogin
to validate the provider accessToken, resolve or create the corresponding
SocialAuth/User, and only then populate the response and generate JWTs using the
real user ID and attributes; remove all mock identity values. Update
src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java:12-16
in loadUserByUsername to look up the user through UserRepository.findById and
throw the appropriate authentication exception when absent, preserving real
authorities and user identity throughout JWT authentication.

In `@src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java`:
- Around line 84-90: Update parseClaims in JwtTokenProvider so expired JWTs
cannot produce usable claims by default; remove the ExpiredJwtException recovery
that returns e.getClaims(), or enforce that parseClaims is only reached after
validateToken. If retaining expired-claim access is required for a refresh flow,
document that intent explicitly and keep it isolated from authentication-token
creation.
- Around line 67-73: Update JwtTokenProvider.getAuthentication to obtain
UserDetails from the configured user-details repository instead of constructing
CustomUserDetails with fixed email and role values. Reuse the existing
UserDetailsService.loadUserByUsername(...) path with the token’s userId, then
create UsernamePasswordAuthenticationToken using the loaded user’s authorities.
- Around line 41-82: Distinguish access and refresh tokens by adding a purpose
claim in createAccessToken and createRefreshToken, using the corresponding
access or refresh value. Update validateToken and the authentication flow around
getAuthentication to require and accept only the access-token type for API
authorization, while refresh-token handling validates only the refresh type
through its dedicated path. Preserve the existing subject, expiry, and signature
behavior.

In
`@src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java`:
- Around line 12-16: Update CustomUserDetailsService.loadUserByUsername() to
load the user by the parsed subject through userRepository, throw
UsernameNotFoundException when absent, and construct CustomUserDetails from the
user’s active status and actual authorities instead of hard-coded values. Update
JwtTokenProvider.getAuthentication() to resolve authentication via
UserDetailsService so valid JWTs still undergo user existence, status, and
permission checks before access is granted.

In `@src/main/java/com/mr/global/security/SecurityUtil.java`:
- Around line 16-24: Update the authentication validation in SecurityUtil to
throw CommonStatus.UNAUTHORIZED when authentication is missing, has no name, or
represents anonymousUser; keep INVALID_INPUT_VALUE for non-numeric authenticated
names unless the existing authentication failure handling requires otherwise.

---

Nitpick comments:
In `@src/main/java/com/mr/global/config/SwaggerConfig.java`:
- Around line 1-4: SwaggerConfig 빈 클래스에 후속 구현 예정임을 나타내는 TODO 주석을 추가하세요. 현재 클래스
구조와 패키지는 유지하고, OpenAPI Bean이나 `@Configuration` 애노테이션 등 범위를 벗어난 변경은 하지 마세요.

In `@src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java`:
- Line 18: Replace the directly instantiated ObjectMapper field in
JwtAuthenticationEntryPoint with constructor injection of the Spring-managed
ObjectMapper bean, removing the new ObjectMapper() initialization so shared
Jackson modules and serialization settings are used.

In `@src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java`:
- Around line 38-44: Update resolveToken to derive the substring start index
from BEARER_PREFIX.length() instead of the hardcoded 7, while preserving the
existing authorization-header validation and token extraction behavior.

In `@src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java`:
- Around line 27-31: Rename the JwtTokenProvider fields
accessTokenValidityInMilliseconds and refreshTokenValidityInMilliseconds to
accessTokenValidityInSeconds and refreshTokenValidityInSeconds, updating all
references while preserving the existing conversion to milliseconds at use
sites. Expose a getter for the configured access-token validity so AuthService
can use it instead of hardcoding tokenExpirationTime as 3600L.
- Around line 41-65: Refactor the duplicated token-building logic in
createAccessToken and createRefreshToken into a private helper that accepts the
token validity duration as a parameter. Keep each public method responsible for
supplying its respective access or refresh validity value, while preserving the
existing claims, timestamps, signing algorithm, and expiration behavior.

In `@src/main/java/com/mr/global/security/SecurityConfig.java`:
- Around line 35-39: In SecurityConfig’s authorizeHttpRequests configuration,
replace the broad /api/v1/auth/** permitAll matcher with explicit matchers for
only the currently public login endpoint, leaving protected
authentication-related endpoints inaccessible by default. Do not broaden the
change to JwtAuthenticationFilter registration unless separately required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a55337c-ca05-4a97-ac3f-7bf981fd717b

📥 Commits

Reviewing files that changed from the base of the PR and between 25e7ef6 and 17e5f51.

📒 Files selected for processing (14)
  • src/main/java/com/mr/domain/auth/controller/AuthController.java
  • src/main/java/com/mr/domain/auth/dto/AuthRequestDTO.java
  • src/main/java/com/mr/domain/auth/dto/AuthResponseDTO.java
  • src/main/java/com/mr/domain/auth/service/AuthService.java
  • src/main/java/com/mr/global/config/SecurityConfig.java
  • src/main/java/com/mr/global/config/SwaggerConfig.java
  • src/main/java/com/mr/global/security/SecurityConfig.java
  • src/main/java/com/mr/global/security/SecurityUtil.java
  • src/main/java/com/mr/global/security/jwt/JwtAccessDeniedHandler.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java
  • src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java
  • src/main/java/com/mr/global/security/principal/CustomUserDetails.java
  • src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java
💤 Files with no reviewable changes (1)
  • src/main/java/com/mr/global/config/SecurityConfig.java

Comment on lines +20 to +32
public AuthResponseDTO.LoginResponse socialLogin(SocialType socialType, String accessToken) {
// 1. 외부 소셜 API (카카오/구글) 통신하여 유저 프로필(email, socialId) 파싱
// SocialUserInfo userInfo = getSocialUserInfo(socialType, accessToken);

// 2. TODO: User 엔티티 연동 및 가입여부 검증 (Stub 구조)
// 만약 가입 안 되어있으면 DB User 생성 -> 저장
Long mockUserId = 1L;
String mockEmail = "user@example.com";
String mockNickname = "뮤즈유저";
boolean isNewUser = false;

String appAccessToken = jwtTokenProvider.createAccessToken(mockUserId);
String appRefreshToken = jwtTokenProvider.createRefreshToken(mockUserId);

@coderabbitai coderabbitai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
git ls-files | rg 'AuthService\.java|CustomUserDetailsService\.java|User\.java|JwtTokenProvider\.java|UserRepository\.java|CustomUserCredentials\.java|spring-security|SecurityConfig\.java' || true

echo
echo "== AuthService excerpt =="
if [ -f src/main/java/com/mr/domain/auth/service/AuthService.java ]; then
  nl -ba src/main/java/com/mr/domain/auth/service/AuthService.java | sed -n '1,220p'
fi

echo
echo "== CustomUserDetailsService excerpt =="
if [ -f src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java ]; then
  nl -ba src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java | sed -n '1,140p'
fi

echo
echo "==Jwt token provider and user-related references =="
rg -n "createAccessToken|createRefreshToken|loadUserByUsername|findById|isActivate|ROLE|SocialType|socialId|accessToken|jwtTokenProvider" src/main/java -S || true

Repository: Musereview/BE

Length of output: 546


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== AuthService excerpt =="
sed -n '1,220p' src/main/java/com/mr/domain/auth/service/AuthService.java | cat -n

echo
echo "== CustomUserDetailsService excerpt =="
sed -n '1,140p' src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java | cat -n

echo
echo "== JwtTokenProvider excerpt =="
sed -n '1,220p' src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java | cat -n

echo
echo "== User entity excerpt =="
sed -n '1,220p' src/main/java/com/mr/domain/user/entity/User.java | cat -n

echo
echo "== Search for user/security/token usages =="
rg -n "createAccessToken|createRefreshToken|loadUserByUsername|findById|isActivate|ROLE|SocialType|socialId|accessToken|jwtTokenProvider|SecurityContextHolder|authentication" src/main/java -S || true

Repository: Musereview/BE

Length of output: 15775


소셜 토큰 검증과 실제 사용자 조회를 연결하세요.

socialLogin이 받은 accessToken을 검증하지 않고 userId=1L로 고정된 JWT를 발급하고, 이후 인증 주체도 실제 User/SocialAuth 조회 없이 고정된 이메일과 ROLE_USER 권한으로 반환됩니다. 이는 어떤 토큰이라도 같은 로그인 응답·권한을 얻을 수 있게 만듭니다.

  • CustomUserDetailsService.loadUserByUsername: userId -> UserRepository.findById와 존재하지 않을 때 예외 반환을 구현하세요. [Spring Security UserDetailsService 참고]
  • AuthService.socialLogin: 소셜 제공자 토큰 검증 → SocialAuth 조회/생성 성공 후에만 DTO의 사용자 속성과 jwtTokenProvider.createAccessToken/RefreshToken(...)을 호출하세요.
  • JWT 클레임도 실제 사용자 ID만 담도록 일관되게 유지하세요.
📍 Affects 2 files
  • src/main/java/com/mr/domain/auth/service/AuthService.java#L20-L32 (this comment)
  • src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java#L12-L16
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/domain/auth/service/AuthService.java` around lines 20 -
32, Update src/main/java/com/mr/domain/auth/service/AuthService.java:20-32 in
socialLogin to validate the provider accessToken, resolve or create the
corresponding SocialAuth/User, and only then populate the response and generate
JWTs using the real user ID and attributes; remove all mock identity values.
Update
src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java:12-16
in loadUserByUsername to look up the user through UserRepository.findById and
throw the appropriate authentication exception when absent, preserving real
authorities and user identity throughout JWT authentication.

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.

다음 pr에서 구현 예정

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@kimyw1018 알겠습니다. 현재 PR의 인프라 구축 범위와 분리하는 방향은 괜찮습니다. 다만 실제 소셜 토큰 검증·사용자 조회 전에는 임의 토큰으로 고정 사용자 JWT가 발급될 수 있으므로, 해당 엔드포인트는 운영 노출 전 반드시 후속 PR에서 완료해 주세요.

원하시면 후속 작업을 추적할 GitHub 이슈를 생성해 드릴까요? 🐰

Comment on lines +34 to +38
AuthResponseDTO.TokenResponse tokenResponse = AuthResponseDTO.TokenResponse.builder()
.accessToken(appAccessToken)
.refreshToken(appRefreshToken)
.tokenExpirationTime(3600L)
.build();

@coderabbitai coderabbitai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

토큰 만료 시간을 JWT 설정과 동일한 소스에서 계산하세요.

JwtTokenProvider는 설정된 accessTokenValidityInMilliseconds를 사용하지만 응답은 항상 3600L을 반환합니다. 설정값이 변경되면 클라이언트가 잘못된 만료 시간을 받아 너무 일찍 또는 늦게 갱신할 수 있습니다. provider에서 실제 TTL을 노출하거나 생성된 JWT의 exp를 기준으로 응답하고, 값의 단위도 명확히 정의하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/mr/domain/auth/service/AuthService.java` around lines 34 -
38, Update the token response construction in AuthService to derive
tokenExpirationTime from the same configured access-token validity used by
JwtTokenProvider instead of the hardcoded 3600L. Expose or reuse the provider’s
actual TTL, ensure the response unit is explicitly consistent with the API
contract, and keep the returned expiration synchronized when JWT settings
change.

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.

다음 pr 에서 구현 예정

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@kimyw1018 알겠습니다. 다음 PR에서 JwtTokenProvider의 실제 access-token TTL을 단일 기준으로 노출·재사용하고, 응답의 tokenExpirationTime 단위도 계약에 명시해 설정 변경 시 동기화되도록 처리하면 됩니다.

원하시면 이 작업을 추적할 GitHub 이슈를 만들어 드릴까요? 🐇

Comment thread src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java
Comment thread src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java
Comment thread src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java
Comment thread src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java Outdated
Comment thread src/main/java/com/mr/global/security/SecurityUtil.java Outdated

@rkdehdrbs7885-oss rkdehdrbs7885-oss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

수고하셨습니다!

final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication == null || authentication.getName() == null || authentication.getName().equals("anonymousUser")) {
throw new GeneralException(CommonStatus.UNAUTHORIZED);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

인증 정보가 없을 때 유입될 수 있는 anonymousUser 상황까지 놓치지 않고 꼼꼼하게 예외 처리해 주셨네요.

@ownue ownue left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

확인 부탁드립니당...ㅎㅎ!!

Comment thread src/main/java/com/mr/domain/auth/controller/AuthController.java Outdated
Comment thread src/main/java/com/mr/domain/auth/controller/AuthController.java
Comment thread src/main/resources/application.example.yml
Comment thread src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java Outdated
Comment thread src/main/java/com/mr/global/security/principal/CustomUserDetails.java Outdated
Comment thread src/main/java/com/mr/global/security/principal/CustomUserDetails.java Outdated
Comment thread src/main/java/com/mr/domain/auth/dto/AuthResponseDTO.java Outdated
Comment thread src/main/java/com/mr/domain/auth/dto/AuthResponseDTO.java Outdated
Comment thread src/main/java/com/mr/domain/auth/controller/AuthController.java

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/mr/global/security/SecurityConfig.java`:
- Line 73: Update SecurityConfig’s configuration.setExposedHeaders call to
remove "Set-Cookie", leaving only headers that browser JavaScript can access;
keep credential handling via allowCredentials(true) unchanged.
- Around line 63-71: Update the CORS configuration in SecurityConfig so
credentialed requests use an explicit, finite production Origin allowlist
instead of the wildcard https://*.musereview.site pattern, while keeping
localhost Origins limited to development profiles. Replace allowedHeaders("*")
with only the headers required by actual requests, including Authorization and
Content-Type, and preserve the existing allowed methods.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fd04c040-c2f7-4dac-8ff6-2175cef1fcec

📥 Commits

Reviewing files that changed from the base of the PR and between a636bb4 and 4530b88.

📒 Files selected for processing (15)
  • MR_config/local/application.example.yml
  • src/main/java/com/mr/domain/auth/controller/AuthController.java
  • src/main/java/com/mr/domain/auth/dto/AuthResponseDTO.java
  • src/main/java/com/mr/domain/auth/entity/enums/SocialType.java
  • src/main/java/com/mr/domain/auth/service/AuthService.java
  • src/main/java/com/mr/domain/user/entity/enums/UserRole.java
  • src/main/java/com/mr/global/security/SecurityConfig.java
  • src/main/java/com/mr/global/security/SecurityUtil.java
  • src/main/java/com/mr/global/security/jwt/JwtAccessDeniedHandler.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java
  • src/main/java/com/mr/global/security/jwt/JwtProperties.java
  • src/main/java/com/mr/global/security/jwt/JwtTokenProvider.java
  • src/main/java/com/mr/global/security/principal/CustomUserDetails.java
  • src/main/java/com/mr/global/security/principal/CustomUserDetailsService.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/java/com/mr/global/security/jwt/JwtAccessDeniedHandler.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationEntryPoint.java
  • src/main/java/com/mr/global/security/jwt/JwtAuthenticationFilter.java
  • src/main/java/com/mr/domain/auth/dto/AuthResponseDTO.java

Comment thread src/main/java/com/mr/global/security/SecurityConfig.java Outdated
Comment thread src/main/java/com/mr/global/security/SecurityConfig.java Outdated

@ownue ownue left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

수고하셨어요! 😍😍

@kimyw1018
kimyw1018 merged commit 274468e into develop Jul 24, 2026
2 checks passed
@kimyw1018
kimyw1018 deleted the feat/#25-social-login-jwt branch July 24, 2026 05:45
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.

✨ Feature - 카카오/구글 소셜 로그인 및 JWT 토큰 인증 기능 구현

3 participants