Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.example.solidconnection.auth.dto.SignUpRequest;
import com.example.solidconnection.auth.dto.oauth.OAuthCodeRequest;
import com.example.solidconnection.auth.dto.oauth.OAuthResponse;
import com.example.solidconnection.auth.dto.oauth.OAuthSignInResponse;
import com.example.solidconnection.auth.service.AuthService;
import com.example.solidconnection.auth.service.CommonSignUpTokenProvider;
import com.example.solidconnection.auth.service.EmailSignInService;
Expand All @@ -21,6 +22,7 @@
import com.example.solidconnection.common.exception.ErrorCode;
import com.example.solidconnection.common.resolver.AuthorizedUser;
import com.example.solidconnection.siteuser.domain.AuthType;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -44,28 +46,39 @@ public class AuthController {
private final EmailSignUpService emailSignUpService;
private final EmailSignUpTokenProvider emailSignUpTokenProvider;
private final CommonSignUpTokenProvider commonSignUpTokenProvider;
private final RefreshTokenCookieManager refreshTokenCookieManager;

@PostMapping("/apple")
public ResponseEntity<OAuthResponse> processAppleOAuth(
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest,
HttpServletResponse httpServletResponse
) {
OAuthResponse oAuthResponse = appleOAuthService.processOAuth(oAuthCodeRequest);
if (oAuthResponse instanceof OAuthSignInResponse signInResponse) {
refreshTokenCookieManager.setCookie(httpServletResponse, signInResponse.refreshToken());
}
return ResponseEntity.ok(oAuthResponse);
}

@PostMapping("/kakao")
public ResponseEntity<OAuthResponse> processKakaoOAuth(
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest
@Valid @RequestBody OAuthCodeRequest oAuthCodeRequest,
HttpServletResponse httpServletResponse
) {
OAuthResponse oAuthResponse = kakaoOAuthService.processOAuth(oAuthCodeRequest);
if (oAuthResponse instanceof OAuthSignInResponse signInResponse) {
refreshTokenCookieManager.setCookie(httpServletResponse, signInResponse.refreshToken());
}
return ResponseEntity.ok(oAuthResponse);
}

@PostMapping("/email/sign-in")
public ResponseEntity<SignInResponse> signInWithEmail(
@Valid @RequestBody EmailSignInRequest signInRequest
@Valid @RequestBody EmailSignInRequest signInRequest,
HttpServletResponse httpServletResponse
) {
SignInResponse signInResponse = emailSignInService.signIn(signInRequest);
refreshTokenCookieManager.setCookie(httpServletResponse, signInResponse.refreshToken());
return ResponseEntity.ok(signInResponse);
}

Expand Down Expand Up @@ -94,20 +107,24 @@ public ResponseEntity<SignInResponse> signUp(

@PostMapping("/sign-out")
public ResponseEntity<Void> signOut(
Authentication authentication
Authentication authentication,
HttpServletResponse httpServletResponse
) {
String accessToken = getAccessToken(authentication);
authService.signOut(accessToken);
refreshTokenCookieManager.deleteCookie(httpServletResponse);
return ResponseEntity.ok().build();
}

@DeleteMapping("/quit")
public ResponseEntity<Void> quit(
@AuthorizedUser long siteUserId,
Authentication authentication,
@AuthorizedUser long siteUserId
HttpServletResponse httpServletResponse
) {
String accessToken = getAccessToken(authentication);
authService.quit(siteUserId, accessToken);
refreshTokenCookieManager.deleteCookie(httpServletResponse);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.solidconnection.auth.controller;

import com.example.solidconnection.auth.domain.TokenType;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Component;

@Component
public class RefreshTokenCookieManager {

private static final String COOKIE_NAME = "refreshToken";
private static final String PATH = "/";
private static final String SAME_SITE = "Strict";

public void setCookie(HttpServletResponse response, String refreshToken) {
long maxAge = convertExpireTimeToCookieMaxAge(TokenType.REFRESH.getExpireTime());
setRefreshTokenCookie(response, refreshToken, maxAge);
}

private long convertExpireTimeToCookieMaxAge(long milliSeconds) {
// jwt의 expireTime: millisecond, cookie의 maxAge: second
return milliSeconds / 1000;
}

public void deleteCookie(HttpServletResponse response) {
setRefreshTokenCookie(response, "", 0); // 쿠키 삭제를 위해 maxAge를 0으로 설정
}

private void setRefreshTokenCookie(
HttpServletResponse response, String refreshToken, long maxAge
) {
ResponseCookie cookie = ResponseCookie.from(COOKIE_NAME, refreshToken)
.httpOnly(true)
.secure(true)
.path(PATH)
.maxAge(maxAge)
.sameSite(SAME_SITE)
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.example.solidconnection.auth.controller;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

import com.example.solidconnection.auth.domain.TokenType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletResponse;

@DisplayName("리프레시 토큰 쿠키 매니저 테스트")
class RefreshTokenCookieManagerTest {

private RefreshTokenCookieManager cookieManager;

@BeforeEach
void setUp() {
cookieManager = new RefreshTokenCookieManager();
}

@Test
void 리프레시_토큰을_쿠키로_설정한다() {
// given
MockHttpServletResponse response = new MockHttpServletResponse();
String refreshToken = "test-refresh-token";

// when
cookieManager.setCookie(response, refreshToken);

// then
String header = response.getHeader("Set-Cookie");
assertAll(
() -> assertThat(header).isNotNull(),
() -> assertThat(header).contains("refreshToken=" + refreshToken),
() -> assertThat(header).contains("HttpOnly"),
() -> assertThat(header).contains("Secure"),
() -> assertThat(header).contains("Path=/"),
() -> assertThat(header).contains("Max-Age=" + TokenType.REFRESH.getExpireTime() / 1000),
() -> assertThat(header).contains("SameSite=Strict")
);
}

@Test
void 쿠키에서_리프레시_토큰을_삭제한다() {
// given
MockHttpServletResponse response = new MockHttpServletResponse();

// when
cookieManager.deleteCookie(response);

// then
String header = response.getHeader("Set-Cookie");
assertAll(
() -> assertThat(header).isNotNull(),
() -> assertThat(header).contains("refreshToken="),
() -> assertThat(header).contains("HttpOnly"),
() -> assertThat(header).contains("Secure"),
() -> assertThat(header).contains("Path=/"),
() -> assertThat(header).contains("Max-Age=0"),
() -> assertThat(header).contains("SameSite=Strict")
);
}
}
Loading