-
Notifications
You must be signed in to change notification settings - Fork 0
[WTH-237] 애플 로그인 수정 및 로그인 시 유저 정보 반환 #44
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
The head ref may contain hidden characters: "refactor/WTH-237-\uB85C\uADF8\uC778-\uC2DC-\uC720\uC800-\uC815\uBCF4-\uBC18\uD658"
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| package com.weeth.domain.user.presentation | ||
|
|
||
| import com.weeth.domain.user.application.usecase.command.SocialLoginUseCase | ||
| import com.weeth.global.auth.jwt.application.service.TokenCookieProvider | ||
| import com.weeth.global.config.properties.OAuthProperties | ||
| import io.swagger.v3.oas.annotations.Hidden | ||
| import org.slf4j.LoggerFactory | ||
| import org.springframework.http.HttpHeaders | ||
| import org.springframework.http.HttpStatus | ||
| import org.springframework.http.MediaType | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.web.bind.annotation.PostMapping | ||
| import org.springframework.web.bind.annotation.RequestParam | ||
| import org.springframework.web.bind.annotation.RestController | ||
| import org.springframework.web.util.UriComponentsBuilder | ||
|
|
||
| /** | ||
| * Apple Sign in with Apple의 form_post 콜백을 처리하는 컨트롤러. | ||
| */ | ||
| @Hidden | ||
| @RestController | ||
| class SocialCallbackController( | ||
| private val socialLoginUseCase: SocialLoginUseCase, | ||
| private val tokenCookieProvider: TokenCookieProvider, | ||
| oAuthProperties: OAuthProperties, | ||
| ) { | ||
| private val log = LoggerFactory.getLogger(javaClass) | ||
| private val frontendRedirectUri = oAuthProperties.apple.frontendRedirectUri | ||
|
|
||
| @PostMapping( | ||
| "/api/v4/users/social/apple/callback", | ||
| consumes = [MediaType.APPLICATION_FORM_URLENCODED_VALUE], | ||
| ) | ||
| fun handleCallback( | ||
| @RequestParam("id_token", required = false) idToken: String?, | ||
| @RequestParam("user", required = false) userJson: String?, | ||
| @RequestParam("error", required = false) error: String?, | ||
| ): ResponseEntity<Void> { | ||
| if (error != null || idToken.isNullOrBlank()) { | ||
| return redirect( | ||
| UriComponentsBuilder | ||
| .fromUriString(frontendRedirectUri) | ||
| .queryParam("error", error ?: "unknown") | ||
| .toUriString(), | ||
| ) | ||
| } | ||
|
|
||
| return try { | ||
| val response = socialLoginUseCase.socialLoginByAppleCallback(idToken, userJson) | ||
|
|
||
| val redirectUri = | ||
| UriComponentsBuilder | ||
| .fromUriString(frontendRedirectUri) | ||
| .queryParam("registered", response.registered) | ||
| .queryParam("name", response.name) | ||
| .toUriString() | ||
|
|
||
| ResponseEntity | ||
| .status(HttpStatus.FOUND) | ||
| .header(HttpHeaders.LOCATION, redirectUri) | ||
| .header( | ||
| HttpHeaders.SET_COOKIE, | ||
| tokenCookieProvider.createAccessTokenCookie(response.accessToken).toString(), | ||
| ).header( | ||
| HttpHeaders.SET_COOKIE, | ||
| tokenCookieProvider.createRefreshTokenCookie(response.refreshToken).toString(), | ||
| ).build() | ||
|
Comment on lines
+58
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: SameSite cookie behavior on a cross-origin POST request followed by a 302 redirect depends on the SameSite attribute value (Strict, Lax, or None) and whether the subsequent redirect request is classified as "same-site" or "cross-site". Cookies set in the 302 response are always stored by the browser before following the redirect (result 1), but whether existing cookies (including those just set) are sent on the follow-up request depends on SameSite rules. Key concepts from specs (results 9,10,13,14,18): - A request is "cross-site" if the initiator site (top-level browsing context) differs from the request URL's site (scheme+registrable domain). - For redirects: HTTP 302 redirects preserve the initiator context, so a cross-site POST keeps the chain cross-site for follow-up requests (results 3,4,22). Sec-Fetch-Site remains "cross-site". - SameSite=Strict: Cookie sent ONLY on same-site requests, never cross-site (results 3,4,9,13). - SameSite=Lax: Sent on same-site requests + cross-site top-level navigations with "safe" methods (GET, HEAD). Cross-site POST or unsafe methods: not sent. Lax-allowing-unsafe mode (some browsers) sends on cross-site top-level regardless of method, but POST after cross-origin POST redirect typically blocked (results 2,5,6,9). - SameSite=None (requires Secure): Sent on all requests, cross-site included (results 9,11). Scenario example (OAuth/login flow, results 5,6): 1. Cross-origin POST to /login (cross-site, initiator from other site). 2. Server responds 302 Location: /dashboard + Set-Cookie (session). 3. Browser stores cookie, follows with GET /dashboard. - If chain cross-site: Strict=None sent; Lax=not sent (not safe method exception); None=sent. - Common breakage with Strict/Lax leads to workarounds like meta-refresh 200 (changes to same-site navigation, result 6). For cross-origin POST 302 specifically: Cookies with Lax/Strict typically NOT sent on redirect leg if chain cross-site; use None for cross-site compatibility. Citations:
🏁 Script executed: # TokenCookieProvider와 쿠키 설정 관련 파일 찾기
fd -t f "TokenCookieProvider\|CookieProperties" --case-sensitiveRepository: Team-Weeth/weeth-server Length of output: 49 🏁 Script executed: # SocialCallbackController 전체 내용 확인
cat -n src/main/kotlin/com/weeth/domain/user/presentation/SocialCallbackController.ktRepository: Team-Weeth/weeth-server Length of output: 4817 🏁 Script executed: # cookieProperties 또는 Cookie 관련 설정 파일 찾기
fd -t f -i "cookie" | grep -i "properties\|config"Repository: Team-Weeth/weeth-server Length of output: 137 🏁 Script executed: # TokenCookieProvider 파일 찾기
fd -t f "TokenCookieProvider"Repository: Team-Weeth/weeth-server Length of output: 240 🏁 Script executed: # CookieProperties 내용 확인
cat -n src/main/kotlin/com/weeth/global/config/properties/CookieProperties.ktRepository: Team-Weeth/weeth-server Length of output: 850 🏁 Script executed: # TokenCookieProvider 내용 확인
cat -n src/main/kotlin/com/weeth/global/auth/jwt/application/service/TokenCookieProvider.ktRepository: Team-Weeth/weeth-server Length of output: 1994 🏁 Script executed: # Verify if there are any alternative token handling methods or other Apple callback implementations
rg -i "apple|samesite" --type kt -A 3 -B 3 | head -80Repository: Team-Weeth/weeth-server Length of output: 93 🏁 Script executed: # Check if there's any frontend token handling via query params or other methods
rg "accessToken|refreshToken" src/main/kotlin/com/weeth/domain/user/presentation/ --type ktRepository: Team-Weeth/weeth-server Length of output: 93 🏁 Script executed: # Verify the scenario more directly
rg "apple" -i --max-count=20Repository: Team-Weeth/weeth-server Length of output: 13812 Apple 콜백의 SameSite 쿠키 이슈로 인해 토큰이 프론트엔드에 전달되지 않습니다. Apple의 해결 방안:
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 백엔드와 프론트 도메인이 맞으면 문제 없지 않아? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이후 백엔드와 프론트엔드가 동일한 registrable domain(e.g., 따라서 두 도메인이 같은 eTLD+1을 공유한다면 제가 지적한 SameSite=Lax 이슈는 해당되지 않습니다. 지적이 맞습니다. ✏️ Learnings added
|
||
| } catch (e: Exception) { | ||
| log.error("Apple 콜백 처리 중 오류 발생", e) | ||
| redirect( | ||
| UriComponentsBuilder | ||
| .fromUriString(frontendRedirectUri) | ||
| .queryParam("error", "login_failed") | ||
| .toUriString(), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| private fun redirect(uri: String): ResponseEntity<Void> = | ||
| ResponseEntity | ||
| .status(HttpStatus.FOUND) | ||
| .header(HttpHeaders.LOCATION, uri) | ||
| .build() | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.