diff --git a/src/main/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManager.java b/src/main/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManager.java index 81bc45461..b0a172e2a 100644 --- a/src/main/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManager.java +++ b/src/main/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManager.java @@ -1,17 +1,21 @@ package com.example.solidconnection.auth.controller; +import com.example.solidconnection.auth.controller.config.RefreshTokenCookieProperties; import com.example.solidconnection.auth.domain.TokenType; import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Component; @Component +@RequiredArgsConstructor public class RefreshTokenCookieManager { private static final String COOKIE_NAME = "refreshToken"; private static final String PATH = "/"; - private static final String SAME_SITE = "Strict"; + + private final RefreshTokenCookieProperties properties; public void setCookie(HttpServletResponse response, String refreshToken) { long maxAge = convertExpireTimeToCookieMaxAge(TokenType.REFRESH.getExpireTime()); @@ -19,7 +23,7 @@ public void setCookie(HttpServletResponse response, String refreshToken) { } private long convertExpireTimeToCookieMaxAge(long milliSeconds) { - // jwt의 expireTime: millisecond, cookie의 maxAge: second + // jwt의 expireTime 단위인 millisecond를 cookie의 maxAge 단위인 second로 변환 return milliSeconds / 1000; } @@ -35,7 +39,8 @@ private void setRefreshTokenCookie( .secure(true) .path(PATH) .maxAge(maxAge) - .sameSite(SAME_SITE) + .domain(properties.cookieDomain()) + .sameSite(properties.sameSite()) .build(); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); } diff --git a/src/main/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookieProperties.java b/src/main/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookieProperties.java new file mode 100644 index 000000000..1fa47d884 --- /dev/null +++ b/src/main/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookieProperties.java @@ -0,0 +1,21 @@ +package com.example.solidconnection.auth.controller.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.server.Cookie.SameSite; + +@ConfigurationProperties(prefix = "token.refresh") +public record RefreshTokenCookieProperties( + String cookieDomain +) { + + public String sameSite() { + if (isDomainSet()) { + return SameSite.STRICT.attributeValue(); // 도메인을 지정한 경우 SameSite=Strict + } + return SameSite.NONE.attributeValue(); // 도메인을 지정하지 않은 경우 SameSite=None + } + + private boolean isDomainSet() { + return cookieDomain != null && !cookieDomain.isBlank(); + } +} diff --git a/src/main/resources/secret b/src/main/resources/secret index e592f6d36..0e9f5d0ce 160000 --- a/src/main/resources/secret +++ b/src/main/resources/secret @@ -1 +1 @@ -Subproject commit e592f6d36f57185c8d92a7838c0e3039603b2c57 +Subproject commit 0e9f5d0cefabab1ab9a306099b457225ce5d641e diff --git a/src/test/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManagerTest.java b/src/test/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManagerTest.java index 944be37ab..91ff13cfa 100644 --- a/src/test/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManagerTest.java +++ b/src/test/java/com/example/solidconnection/auth/controller/RefreshTokenCookieManagerTest.java @@ -2,21 +2,35 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.BDDMockito.given; +import com.example.solidconnection.auth.controller.config.RefreshTokenCookieProperties; import com.example.solidconnection.auth.domain.TokenType; +import com.example.solidconnection.support.TestContainerSpringBootTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.mock.web.MockHttpServletResponse; @DisplayName("리프레시 토큰 쿠키 매니저 테스트") +@TestContainerSpringBootTest class RefreshTokenCookieManagerTest { + @Autowired private RefreshTokenCookieManager cookieManager; + @MockBean + private RefreshTokenCookieProperties refreshTokenCookieProperties; + + private final String sameSite = "Strict"; + private final String domain = "example.com"; + @BeforeEach void setUp() { - cookieManager = new RefreshTokenCookieManager(); + given(refreshTokenCookieProperties.cookieDomain()).willReturn(domain); + given(refreshTokenCookieProperties.sameSite()).willReturn(sameSite); } @Test @@ -37,7 +51,8 @@ void setUp() { () -> assertThat(header).contains("Secure"), () -> assertThat(header).contains("Path=/"), () -> assertThat(header).contains("Max-Age=" + TokenType.REFRESH.getExpireTime() / 1000), - () -> assertThat(header).contains("SameSite=Strict") + () -> assertThat(header).contains("Domain=" + domain), + () -> assertThat(header).contains("SameSite=" + sameSite) ); } @@ -58,7 +73,9 @@ void setUp() { () -> assertThat(header).contains("Secure"), () -> assertThat(header).contains("Path=/"), () -> assertThat(header).contains("Max-Age=0"), - () -> assertThat(header).contains("SameSite=Strict") + () -> assertThat(header).contains("SameSite=Strict"), + () -> assertThat(header).contains("Domain=" + domain), + () -> assertThat(header).contains("SameSite=" + sameSite) ); } } diff --git a/src/test/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookiePropertiesTest.java b/src/test/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookiePropertiesTest.java new file mode 100644 index 000000000..0a62541b1 --- /dev/null +++ b/src/test/java/com/example/solidconnection/auth/controller/config/RefreshTokenCookiePropertiesTest.java @@ -0,0 +1,35 @@ +package com.example.solidconnection.auth.controller.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.web.server.Cookie.SameSite; + +@DisplayName("리프레시 토큰 쿠키 설정 테스트") +class RefreshTokenCookiePropertiesTest { + + @Test + void Domain을_지정했으면_SameSite가_Strict() { + // given + RefreshTokenCookieProperties properties = new RefreshTokenCookieProperties("example.com"); + + // when + String sameSite = properties.sameSite(); + + // then + assertThat(sameSite).isEqualTo(SameSite.STRICT.attributeValue()); + } + + @Test + void Domain을_지정하지_않았으면_SameSite가_None() { + // given + RefreshTokenCookieProperties properties = new RefreshTokenCookieProperties(null); + + // when + String sameSite = properties.sameSite(); + + // then + assertThat(sameSite).isEqualTo(SameSite.NONE.attributeValue()); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 7abc6949f..ce5a848cb 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -85,3 +85,6 @@ cors: - "http://localhost:8080" news: default-thumbnail-url: "default-thumbnail-url" +token: + refresh: + cookie-domain: "test.domain.com"