diff --git a/backend/http/auth.http b/backend/http/auth.http index dcf1b5003..0b384a1f0 100644 --- a/backend/http/auth.http +++ b/backend/http/auth.http @@ -11,7 +11,7 @@ POST {{base-url}}/auth/signup Content-Type: application/json { - "ci": "poke12345678" + "ci": "poke123456783" } ### 토큰 재발급 API diff --git a/backend/src/main/java/com/zzang/chongdae/auth/controller/AuthController.java b/backend/src/main/java/com/zzang/chongdae/auth/controller/AuthController.java index 91e18e84d..26c821afb 100644 --- a/backend/src/main/java/com/zzang/chongdae/auth/controller/AuthController.java +++ b/backend/src/main/java/com/zzang/chongdae/auth/controller/AuthController.java @@ -2,14 +2,12 @@ import com.zzang.chongdae.auth.service.AuthService; import com.zzang.chongdae.auth.service.dto.LoginRequest; +import com.zzang.chongdae.auth.service.dto.LoginResponse; import com.zzang.chongdae.auth.service.dto.RefreshRequest; +import com.zzang.chongdae.auth.service.dto.RefreshResponse; import com.zzang.chongdae.auth.service.dto.SignupRequest; import com.zzang.chongdae.auth.service.dto.SignupResponse; -import com.zzang.chongdae.auth.service.dto.TokenDto; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -21,16 +19,12 @@ public class AuthController { private final AuthService authService; - private final CookieProducer cookieExtractor; - private final CookieConsumer cookieConsumer; @PostMapping("/auth/login") - public ResponseEntity login( - @RequestBody @Valid LoginRequest request, HttpServletResponse servletResponse) { - TokenDto tokenDto = authService.login(request); - List cookies = cookieExtractor.extractAuthCookies(tokenDto); - cookieConsumer.addCookies(servletResponse, cookies); - return ResponseEntity.ok().build(); + public ResponseEntity login( + @RequestBody @Valid LoginRequest request) { + LoginResponse response = authService.login(request); + return ResponseEntity.ok(response); } @PostMapping("/auth/signup") @@ -41,11 +35,9 @@ public ResponseEntity signup( } @PostMapping("/auth/refresh") - public ResponseEntity refresh( - @RequestBody RefreshRequest request, HttpServletResponse servletResponse) { - TokenDto tokenDto = authService.refresh(request); - List cookies = cookieExtractor.extractAuthCookies(tokenDto); - cookieConsumer.addCookies(servletResponse, cookies); - return ResponseEntity.ok().build(); + public ResponseEntity refresh( + @RequestBody RefreshRequest request) { + RefreshResponse response = authService.refresh(request); + return ResponseEntity.ok(response); } } diff --git a/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieConsumer.java b/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieConsumer.java index 3debb9907..bfae11f71 100644 --- a/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieConsumer.java +++ b/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieConsumer.java @@ -3,9 +3,7 @@ import com.zzang.chongdae.auth.exception.AuthErrorCode; import com.zzang.chongdae.global.exception.MarketException; import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletResponse; import java.util.Arrays; -import java.util.List; import org.springframework.stereotype.Component; @Component @@ -14,12 +12,6 @@ public class CookieConsumer { private static final String ACCESS_TOKEN_COOKIE_NAME = "access_token"; private static final String REFRESH_TOKEN_COOKIE_NAME = "refresh_token"; - public void addCookies(HttpServletResponse servletResponse, List cookies) { - for (Cookie cookie : cookies) { - servletResponse.addCookie(cookie); - } - } - public String getAccessToken(Cookie[] cookies) { return getTokenByCookieName(ACCESS_TOKEN_COOKIE_NAME, cookies); } diff --git a/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieProducer.java b/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieProducer.java deleted file mode 100644 index 0b9517766..000000000 --- a/backend/src/main/java/com/zzang/chongdae/auth/controller/CookieProducer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.zzang.chongdae.auth.controller; - -import com.zzang.chongdae.auth.service.dto.TokenDto; -import jakarta.servlet.http.Cookie; -import java.util.ArrayList; -import java.util.List; -import org.springframework.stereotype.Component; - -@Component -public class CookieProducer { - - private static final String ACCESS_TOKEN_COOKIE_NAME = "access_token"; - private static final String REFRESH_TOKEN_COOKIE_NAME = "refresh_token"; - - public List extractAuthCookies(TokenDto tokenDto) { - List cookies = new ArrayList<>(); - cookies.add(createCookie(ACCESS_TOKEN_COOKIE_NAME, tokenDto.accessToken())); - cookies.add(createCookie(REFRESH_TOKEN_COOKIE_NAME, tokenDto.refreshToken())); - return cookies; - } - - private Cookie createCookie(String tokenName, String token) { - Cookie cookie = new Cookie(tokenName, token); - cookie.setHttpOnly(true); - cookie.setPath("/"); - return cookie; - } -} diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/AuthService.java b/backend/src/main/java/com/zzang/chongdae/auth/service/AuthService.java index 5f39bfabc..ff6f5733d 100644 --- a/backend/src/main/java/com/zzang/chongdae/auth/service/AuthService.java +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/AuthService.java @@ -2,7 +2,9 @@ import com.zzang.chongdae.auth.exception.AuthErrorCode; import com.zzang.chongdae.auth.service.dto.LoginRequest; +import com.zzang.chongdae.auth.service.dto.LoginResponse; import com.zzang.chongdae.auth.service.dto.RefreshRequest; +import com.zzang.chongdae.auth.service.dto.RefreshResponse; import com.zzang.chongdae.auth.service.dto.SignupRequest; import com.zzang.chongdae.auth.service.dto.SignupResponse; import com.zzang.chongdae.auth.service.dto.TokenDto; @@ -24,11 +26,12 @@ public class AuthService { private final JwtTokenProvider jwtTokenProvider; private final NicknameGenerator nickNameGenerator; - public TokenDto login(LoginRequest request) { + public LoginResponse login(LoginRequest request) { String password = passwordEncoder.encode(request.ci()); MemberEntity member = memberRepository.findByPassword(password) .orElseThrow(() -> new MarketException(AuthErrorCode.INVALID_PASSWORD)); - return jwtTokenProvider.createAuthToken(member.getId().toString()); + TokenDto tokenDto = jwtTokenProvider.createAuthToken(member.getId().toString()); + return new LoginResponse(tokenDto); } @Transactional @@ -39,12 +42,14 @@ public SignupResponse signup(SignupRequest request) { } MemberEntity member = new MemberEntity(nickNameGenerator.generate(), password); MemberEntity savedMember = memberRepository.save(member); - return new SignupResponse(savedMember); + TokenDto tokenDto = jwtTokenProvider.createAuthToken(savedMember.getId().toString()); + return new SignupResponse(savedMember, tokenDto); } - public TokenDto refresh(RefreshRequest request) { + public RefreshResponse refresh(RefreshRequest request) { Long memberId = jwtTokenProvider.getMemberIdByRefreshToken(request.refreshToken()); - return jwtTokenProvider.createAuthToken(memberId.toString()); + TokenDto tokenDto = jwtTokenProvider.createAuthToken(memberId.toString()); + return new RefreshResponse(tokenDto); } public MemberEntity findMemberByAccessToken(String token) { diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/LoginResponse.java b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/LoginResponse.java new file mode 100644 index 000000000..dda385fdd --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/LoginResponse.java @@ -0,0 +1,8 @@ +package com.zzang.chongdae.auth.service.dto; + +public record LoginResponse(String accessToken, String refreshToken) { + + public LoginResponse(TokenDto tokenDto) { + this(tokenDto.accessToken(), tokenDto.refreshToken()); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/RefreshResponse.java b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/RefreshResponse.java new file mode 100644 index 000000000..ccd0bf86e --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/RefreshResponse.java @@ -0,0 +1,8 @@ +package com.zzang.chongdae.auth.service.dto; + +public record RefreshResponse(String accessToken, String refreshToken) { + + public RefreshResponse(TokenDto tokenDto) { + this(tokenDto.accessToken(), tokenDto.refreshToken()); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupMemberResponseItem.java b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupMemberResponseItem.java new file mode 100644 index 000000000..eaa0aa6d0 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupMemberResponseItem.java @@ -0,0 +1,10 @@ +package com.zzang.chongdae.auth.service.dto; + +import com.zzang.chongdae.member.repository.entity.MemberEntity; + +public record SignupMemberResponseItem(Long id, String nickname) { + + public SignupMemberResponseItem(MemberEntity member) { + this(member.getId(), member.getNickname()); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupResponse.java b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupResponse.java index 69637e901..18b5d4c45 100644 --- a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupResponse.java +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupResponse.java @@ -2,9 +2,9 @@ import com.zzang.chongdae.member.repository.entity.MemberEntity; -public record SignupResponse(Long memberId, String nickname) { +public record SignupResponse(SignupMemberResponseItem member, SignupTokenResponseItem token) { - public SignupResponse(MemberEntity member) { - this(member.getId(), member.getNickname()); + public SignupResponse(MemberEntity savedMember, TokenDto tokenDto) { + this(new SignupMemberResponseItem(savedMember), new SignupTokenResponseItem(tokenDto)); } } diff --git a/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupTokenResponseItem.java b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupTokenResponseItem.java new file mode 100644 index 000000000..1bbc56b66 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/auth/service/dto/SignupTokenResponseItem.java @@ -0,0 +1,8 @@ +package com.zzang.chongdae.auth.service.dto; + +public record SignupTokenResponseItem(String accessToken, String refreshToken) { + + public SignupTokenResponseItem(TokenDto tokenDto) { + this(tokenDto.accessToken(), tokenDto.refreshToken()); + } +} diff --git a/backend/src/test/java/com/zzang/chongdae/auth/integration/AuthIntegrationTest.java b/backend/src/test/java/com/zzang/chongdae/auth/integration/AuthIntegrationTest.java index 9e5c77bbd..4ff0a9b65 100644 --- a/backend/src/test/java/com/zzang/chongdae/auth/integration/AuthIntegrationTest.java +++ b/backend/src/test/java/com/zzang/chongdae/auth/integration/AuthIntegrationTest.java @@ -1,6 +1,5 @@ package com.zzang.chongdae.auth.integration; -import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; import static com.epages.restdocs.apispec.ResourceDocumentation.resource; import static com.epages.restdocs.apispec.ResourceSnippetParameters.builder; import static com.epages.restdocs.apispec.Schema.schema; @@ -8,7 +7,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; -import com.epages.restdocs.apispec.HeaderDescriptorWithType; import com.epages.restdocs.apispec.ResourceSnippetParameters; import com.zzang.chongdae.auth.service.dto.LoginRequest; import com.zzang.chongdae.auth.service.dto.RefreshRequest; @@ -37,18 +35,17 @@ class Login { List requestDescriptors = List.of( fieldWithPath("ci").description("회원 식별자 인증 정보") ); - List responseHeaderDescriptors = List.of( - headerWithName("Set-Cookie").description(""" - access_token=a.b.c; Path=/; HttpOnly \n - refresh_token=a.b.c; Path=/; HttpOnly - """) + List responseDescriptors = List.of( + fieldWithPath("accessToken").description("accessToken"), + fieldWithPath("refreshToken").description("refreshToken") ); ResourceSnippetParameters successSnippets = builder() .summary("회원 로그인") .description("회원 식별자 인증 정보로 로그인 합니다.") .requestFields(requestDescriptors) + .responseFields(responseDescriptors) .requestSchema(schema("LonginRequest")) - .responseHeaders(responseHeaderDescriptors) + .responseSchema(schema("LoginResponse")) .build(); MemberEntity member; @@ -82,8 +79,10 @@ class Signup { fieldWithPath("ci").description("회원 식별자 인증 정보") ); List responseDescriptors = List.of( - fieldWithPath("memberId").description("회원 id"), - fieldWithPath("nickname").description("닉네임") + fieldWithPath("member.id").description("회원 id"), + fieldWithPath("member.nickname").description("닉네임"), + fieldWithPath("token.accessToken").description("accessToken"), + fieldWithPath("token.refreshToken").description("refreshToken") ); ResourceSnippetParameters successSnippets = builder() .summary("회원 가입") @@ -144,20 +143,19 @@ void should_throwException_when_givenAlreadyExistMember() { class Refresh { List requestDescriptors = List.of( - fieldWithPath("refreshToken").description("재발급에 필요한 토큰") + fieldWithPath("refreshToken").description("재발급에 필요한 refreshToken") ); - List responseHeaderDescriptors = List.of( - headerWithName("Set-Cookie").description(""" - access_token=a.b.c; Path=/; HttpOnly \n - refresh_token=a.b.c; Path=/; HttpOnly - """) + List responseDescriptors = List.of( + fieldWithPath("accessToken").description("재발급한 accessToken"), + fieldWithPath("refreshToken").description("재발급한 refreshToken") ); ResourceSnippetParameters successSnippets = builder() .summary("토큰 재발급") .description("토큰을 재발급합니다.") .requestFields(requestDescriptors) - .responseHeaders(responseHeaderDescriptors) + .responseFields(responseDescriptors) .requestSchema(schema("RefreshRequest")) + .requestSchema(schema("RefreshResponse")) .build(); ResourceSnippetParameters failedSnippets = builder() .requestFields(requestDescriptors) diff --git a/backend/src/test/java/com/zzang/chongdae/global/helper/CookieProvider.java b/backend/src/test/java/com/zzang/chongdae/global/helper/CookieProvider.java index 9a9c3e815..28bd9fe35 100644 --- a/backend/src/test/java/com/zzang/chongdae/global/helper/CookieProvider.java +++ b/backend/src/test/java/com/zzang/chongdae/global/helper/CookieProvider.java @@ -1,29 +1,35 @@ package com.zzang.chongdae.global.helper; -import com.zzang.chongdae.auth.service.dto.LoginRequest; -import io.restassured.RestAssured; +import com.zzang.chongdae.auth.service.dto.TokenDto; +import io.restassured.http.Cookie; import io.restassured.http.Cookies; -import org.springframework.http.MediaType; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; + @Component public class CookieProvider { + private final TestTokenProvider tokenProvider; + + @Autowired + public CookieProvider(TestTokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + public Cookies createCookies() { - return RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(new LoginRequest("dora1234")) - .when().post("/auth/login") - .then().log().all() - .extract().detailedCookies(); + TokenDto tokenDto = tokenProvider.createTokens(); + return createCookiesFromTokenDto(tokenDto); } public Cookies createCookiesWithCi(String ci) { - return RestAssured.given().log().all() - .contentType(MediaType.APPLICATION_JSON_VALUE) - .body(new LoginRequest(ci)) - .when().post("/auth/login") - .then().log().all() - .extract().detailedCookies(); + TokenDto tokenDto = tokenProvider.createTokensWithCi(ci); + return createCookiesFromTokenDto(tokenDto); + } + + private Cookies createCookiesFromTokenDto(TokenDto tokenDto) { + Cookie accessToken = new Cookie.Builder("access_token", tokenDto.accessToken()).build(); + Cookie refreshToken = new Cookie.Builder("refresh_token", tokenDto.refreshToken()).build(); + return new Cookies(accessToken, refreshToken); } } diff --git a/backend/src/test/java/com/zzang/chongdae/global/helper/TestTokenProvider.java b/backend/src/test/java/com/zzang/chongdae/global/helper/TestTokenProvider.java new file mode 100644 index 000000000..9594898d5 --- /dev/null +++ b/backend/src/test/java/com/zzang/chongdae/global/helper/TestTokenProvider.java @@ -0,0 +1,29 @@ +package com.zzang.chongdae.global.helper; + +import com.zzang.chongdae.auth.service.dto.LoginRequest; +import com.zzang.chongdae.auth.service.dto.TokenDto; +import io.restassured.RestAssured; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; + +@Component +public class TestTokenProvider { + + public TokenDto createTokens() { // TODO: 로그인 API 호출 대신 토큰 생성 로직으로 대체하기 + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new LoginRequest("dora1234")) + .when().post("/auth/login") + .then().log().all() + .extract().response().as(TokenDto.class); + } + + public TokenDto createTokensWithCi(String ci) { + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new LoginRequest(ci)) + .when().post("/auth/login") + .then().log().all() + .extract().response().as(TokenDto.class); + } +}