-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor social authentication system with enhanced security and structure #144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4ebdf04
Refactor authentication API and token management system
youngjun-k 446884e
Refactor error handling and improve code clarity
youngjun-k 8089ed2
Refactor authentication flow and enhance security measures
youngjun-k bcbf391
Merge branch 'develop' of https://github.com/juulabel/juulabel-back i…
youngjun-k ff10f8b
Refactor SocialLinkService by removing unused imports and redundant c…
youngjun-k be27ffa
Enhance token management and cookie handling in authentication service
youngjun-k File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| # 인증 시스템 보안 강화 및 아키텍처 리팩토링 (PR [#142](https://github.com/juulabel/juulabel-back/pull/144)) | ||
|
|
||
| ## TL;DR | ||
|
|
||
| 본 PR은 소셜 인증 시스템의 종합적인 보안 개선을 구현하여, 여러 중요한 보안 취약점을 해결하고 전체적인 아키텍처를 개선합니다. CSRF 보호, 토큰 분리, 그리고 간소화된 API 계약을 도입합니다. | ||
|
|
||
| 1. **보안 강화**: CSRF 보호 및 안전한 쿠키 처리 구현 | ||
| 2. **아키텍처 단순화**: API 표면 영역 축소 및 미사용 필드 제거 | ||
| 3. **토큰 보안**: 토큰 시크릿 분리 및 적절한 토큰 생명주기 관리 | ||
| 4. **사용자 경험**: 보안 강화를 유지하면서 원활한 인증 플로우 제공 | ||
|
|
||
| ## 🔧 기술적 변경사항 | ||
|
|
||
| ### 1. 로그인 엔드포인트 리팩토링 (`/v1/api/auth/login/{provider}`) | ||
|
|
||
| #### 응답 바디 최적화 | ||
|
|
||
| **변경 전**: 미사용 필드가 포함된 복잡한 응답 | ||
| **변경 후**: 필수 데이터 플로우에 집중한 간소화된 응답 | ||
|
|
||
| ```java | ||
| public record LoginResponse( | ||
| String accessToken, // 기존 사용자의 경우 제공, 신규 사용자의 경우 null | ||
| String signUpToken, // 신규 사용자의 경우 제공, 기존 사용자의 경우 null | ||
| String email // 식별을 위해 항상 제공 | ||
| ) {} | ||
| ``` | ||
|
|
||
| **근거**: | ||
|
|
||
| - 미사용 필드를 통한 데이터 누출 방지 | ||
| - 기존 사용자 로그인과 신규 사용자 회원가입 플로우의 명확한 분리 | ||
| - 클라이언트 측 상태 관리 단순화 | ||
|
|
||
| #### CSRF 보호 구현 | ||
|
|
||
| - **쿠키**: 로그인 시 `CSRF-TOKEN`을 쿠키로 저장 | ||
| - **사용처**: 후속 토큰 갱신 작업에 필요 | ||
| - **보안**: 민감한 토큰 작업에 대한 CSRF 공격 방지 | ||
|
|
||
| ### 2. 회원가입 엔드포인트 강화 (`/v1/api/auth/sign-up`) | ||
|
|
||
| #### Authorization 헤더 요구사항 | ||
|
|
||
| ```http | ||
| Authorization: Bearer {signUpToken} | ||
| ``` | ||
|
|
||
| **보안 기능**: | ||
|
|
||
| - **토큰 검증**: 회원가입 토큰이 로그인 세션 데이터와 일치하는지 확인 | ||
| - **시간 검증**: 15분 만료 기간 (초과 시 401 반환) | ||
| - **무결성 검사**: 토큰 불일치/변조 시 403 반환 | ||
|
|
||
| #### 요청 바디 단순화 | ||
|
|
||
| **제거된 필드** (현재 signUpToken 페이로드에 포함): | ||
|
|
||
| - `email` - 토큰 페이로드에서 추출 | ||
| - `provider` - 토큰 페이로드에서 추출 | ||
| - `providerId` - 토큰 페이로드에서 추출 | ||
|
|
||
| **이점**: | ||
|
|
||
| - 요청과 토큰 간의 데이터 불일치 방지 | ||
| - 데이터 조작에 대한 공격 표면 축소 | ||
| - 단일 진실 소스 원칙 적용 | ||
|
|
||
| #### 응답 최적화 | ||
|
|
||
| ```java | ||
| public record SignUpMemberResponse( | ||
| Long memberId, | ||
| String accessToken | ||
| ) {} | ||
| ``` | ||
|
|
||
| ### 3. 토큰 갱신 보안 (`/v1/api/auth/refresh`) | ||
|
|
||
| #### CSRF 헤더 검증 | ||
|
|
||
| ```http | ||
| X-CSRF-TOKEN: {csrfToken} | ||
| ``` | ||
|
|
||
| **구현 세부사항**: | ||
|
|
||
| - 쿠키 값과 헤더 값이 일치해야 함 | ||
| - 각 요청마다 토큰 자동 갱신 | ||
| - 토큰 누락/무효 시 403 오류 반환 | ||
|
|
||
| #### 보안 설정 | ||
|
|
||
| ```java | ||
| .csrf(csrf -> csrf | ||
| .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) | ||
| .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler()) | ||
| .requireCsrfProtectionMatcher(request -> | ||
| request.getServletPath().equals("/v1/api/auth/refresh"))) | ||
| ``` | ||
|
|
||
| ## 🔐 보안 강화사항 | ||
|
|
||
| ### 토큰 시크릿 분리 | ||
|
|
||
| - **Access Token**: 사용자 세션 전용 시크릿 | ||
| - **Refresh Token**: 토큰 갱신 전용 시크릿 | ||
| - **Signup Token**: 등록 플로우 전용 시크릿 | ||
|
|
||
| **이점**: 시크릿 침해 시 피해 범위 제한 | ||
|
|
||
| ## 🔄 마이그레이션 전략 | ||
|
|
||
| ### Breaking Changes | ||
|
|
||
| 1. **API 계약 변경**: 클라이언트 애플리케이션이 새 응답 형식으로 업데이트 필요 | ||
| 2. **헤더 요구사항**: 회원가입 요청에 Authorization 헤더 필요 | ||
| 3. **CSRF 헤더**: 갱신 요청에 X-CSRF-TOKEN 헤더 필요 | ||
|
|
||
| ### 호환성 고려사항 | ||
|
|
||
| - 전환 기간 동안 기존 액세스 토큰 유효 유지 | ||
| - 프로덕션 배포 시 점진적 롤아웃 권장 | ||
| - 헤더 관리를 위한 클라이언트 측 변경 필요 | ||
|
|
||
| ## 🚀 향후 고려사항 | ||
|
|
||
| ### OAuth 2.0 PKCE 구현 | ||
|
|
||
| - **상태**: 다음 반복에서 계획됨 | ||
| - **접근방식**: 프론트엔드 팀과 협력하여 클라이언트 측 구현 | ||
| - **이점**: 공개 OAuth 클라이언트에 대한 보안 강화 | ||
|
|
||
| ### 쿠키 보안 강화 | ||
|
|
||
| **현재 구현**: 도메인 마이그레이션 전략 | ||
|
|
||
| - **기존**: `juulabel.com/app` | ||
| - **변경**: `m.juulabel.com` | ||
| - **API 도메인**: `api.juulabel.com` | ||
| - **SameSite**: CSRF 보호 강화를 위한 Strict 정책 | ||
|
|
||
| --- | ||
|
|
||
| **리뷰 체크리스트**: | ||
|
|
||
| - [ ] CSRF 보호 검증, 엔드투엔드 인증 플로우 | ||
| - [ ] 도메인 마이그레이션 전략 검토 | ||
| - [ ] 모든 인증 플로우에 대한 QA 검증 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,5 @@ | ||
| package com.juu.juulabel.admin; | ||
|
|
||
|
|
||
| import com.juu.juulabel.common.provider.JwtTokenProvider; | ||
| import com.juu.juulabel.member.domain.Member; | ||
| import com.juu.juulabel.member.service.MemberService; | ||
| import io.swagger.v3.oas.annotations.Operation; | ||
|
|
@@ -10,26 +8,21 @@ | |
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import com.juu.juulabel.common.provider.jwt.AccessTokenProvider; | ||
|
|
||
| @Tag( | ||
| name = "테스트 API", | ||
| description = "테스트 API" | ||
| ) | ||
| @Tag(name = "테스트 API", description = "테스트 API") | ||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class TestAccessTokenController { | ||
|
|
||
| private final JwtTokenProvider jwtTokenProvider; | ||
| private final AccessTokenProvider accessTokenProvider; | ||
| private final MemberService memberService; | ||
|
|
||
| @Operation( | ||
| summary = "JWT 테스트용 토큰 발급 API", | ||
| description = "기본 [email protected] 이메일로 JWT 발급" | ||
| ) | ||
| @Operation(summary = "JWT 테스트용 토큰 발급 API", description = "기본 [email protected] 이메일로 JWT 발급") | ||
| @GetMapping("/token") | ||
| public String testAccessToken(@RequestParam(defaultValue = "[email protected]") String email) { | ||
| Member member = memberService.getMemberByEmail(email); | ||
| return jwtTokenProvider.createAccessToken(member); | ||
| return accessTokenProvider.createToken(member); | ||
| } | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
src/main/java/com/juu/juulabel/auth/domain/SignUpToken.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.juu.juulabel.auth.domain; | ||
|
|
||
| import com.juu.juulabel.member.domain.Provider; | ||
|
|
||
| public record SignUpToken( | ||
| String token, | ||
| String email, | ||
| Provider provider, | ||
| String providerId, | ||
| String nonce) { | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
로그인 메서드 시그니처 변경사항을 확인해야 해, fool!
메서드명이
oauthLogin에서login으로 변경되고 CSRF 토큰 매개변수가 추가됐어. 하지만 36번 라인에서csrfToken.getToken()을 호출하고 반환값을 사용하지 않는 것이 의심스러워.CSRF 토큰 검증이 제대로 작동하는지 확인이 필요해. I pity the fool who calls getToken() without proper validation!
다음 스크립트로 AuthApiDocs 인터페이스와 시그니처가 일치하는지 확인해:
🏁 Script executed:
Length of output: 87
다음 스크립트로 AuthApiDocs 인터페이스와 AuthController의
login시그니처를 직접 비교해보자, fool!🏁 Script executed:
Length of output: 767
다시 검증하자, fool! 괄호를 제대로 이스케이프하고 AuthApiDocs의
login시그니처를 확인해야 해—I pity the fool who skips this!🏁 Script executed:
Length of output: 732
다음으로 AuthApiDocs의
login시그니처 전체를 확인해보자, fool! I pity the fool who skips this!🏁 Script executed:
Length of output: 1186
CSRF 토큰 호출 로직 검증 필요, fool!
AuthController.java:36에서
csrfToken.getToken()을 호출만 하고 반환값을 사용하지 않음. 이 상태로는 CSRF 유효성 검증이나 토큰 전송이 처리되지 않습니다. 아래 조치 중 하나를 선택하세요, I pity the fool who ignores this!CsrfFilter에서 처리된다면 메서드 시그니처와getToken()호출 제거csrfToken.getToken()값을 설정하는 로직 추가참고: AuthApiDocs 인터페이스(46
48행)와 AuthController(3234행)의login시그니처는 일치합니다.🤖 Prompt for AI Agents