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
36 changes: 35 additions & 1 deletion src/docs/asciidoc/auth-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ include::{snippetsDir}/loginUser/1/http-request.adoc[]
==== 성공 Response
include::{snippetsDir}/loginUser/1/http-response.adoc[]

==== Response Headers
include::{snippetsDir}/loginUser/1/response-headers.adoc[]

==== Response Body Fields
include::{snippetsDir}/loginUser/1/response-fields.adoc[]

Expand All @@ -26,13 +29,44 @@ include::{snippetsDir}/kakaoLogin/1/http-request.adoc[]
==== 성공 Response
성공 1. 카카오 로그인 성공, 서비스에 가입된 유저인 경우
include::{snippetsDir}/kakaoLogin/1/http-response.adoc[]
include::{snippetsDir}/kakaoLogin/1/response-fields.adoc[]

성공 2. 카카오 로그인은 성공했지만, 서비스에 가입된 유저가 아닌 경우 (세션에 카카오 유저 정보를 임시로 등록하고 응답 쿠키에 SESSION 등록, 만료시간 5분)
include::{snippetsDir}/kakaoLogin/2/http-response.adoc[]

==== Response Headers
성공 1.
include::{snippetsDir}/kakaoLogin/1/response-headers.adoc[]

성공 2.
include::{snippetsDir}/kakaoLogin/2/response-headers.adoc[]

==== Response Body Fields
성공 1.
include::{snippetsDir}/kakaoLogin/1/response-fields.adoc[]

==== 실패 Response
실패 1.
include::{snippetsDir}/kakaoLogin/3/http-response.adoc[]
실패 2.
include::{snippetsDir}/kakaoLogin/4/http-response.adoc[]

---

=== **3. 로그아웃**

로그아웃 api 입니다. 요청 헤더에 Key=Cookie, Value="SESSION=" 형식의 세션 쿠키가 존재해야합니다.

==== Request
include::{snippetsDir}/logout/1/http-request.adoc[]

==== Request Header
include::{snippetsDir}/logout/1/request-headers.adoc[]

==== 성공 Response
include::{snippetsDir}/logout/1/http-response.adoc[]

==== Response Body Fields
include::{snippetsDir}/logout/1/response-fields.adoc[]

==== 실패 Response
include::{snippetsDir}/logout/2/http-response.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ftm.server.web.controller.auth;

import com.ftm.server.common.response.ApiResponse;
import com.ftm.server.common.response.enums.SuccessResponseCode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class LogoutController {

@PostMapping("/api/auth/logout")
public ResponseEntity<ApiResponse<Void>> logout(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if (session != null) {
session.invalidate();
}

return ResponseEntity.status(HttpStatus.OK)
.body(ApiResponse.success(SuccessResponseCode.OK));
}
}
11 changes: 7 additions & 4 deletions src/test/java/com/ftm/server/auth/KakaoLoginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private RestDocumentationResultHandler getDocument(
MvcResult result = resultActions.andReturn();

// 세션 쿠키 수동 추가 (문서화 통과용)
result.getResponse().addHeader("Set-Cookie", "SESSION=mock-session-id; Path=/; HttpOnly");
result.getResponse().addHeader("Set-Cookie", "SESSION=session-id; Path=/; HttpOnly");

// then
resultActions
Expand All @@ -124,7 +124,8 @@ private RestDocumentationResultHandler getDocument(
.andDo(print());

// documentation
resultActions.andDo(getDocument(1, "SESSION 쿠키: 로그인 성공 시 발급되는 세션 쿠키 (기본 만료 시간 30분)"));
resultActions.andDo(
getDocument(1, "로그인 성공 시 발급되는 세션 쿠키 (만료 시간: 1시간, Value: SESSION=session-id 형태)"));
}

@Test
Expand All @@ -140,7 +141,7 @@ private RestDocumentationResultHandler getDocument(
MvcResult result = resultActions.andReturn();

// 세션 쿠키 수동 추가 (문서화 통과용)
result.getResponse().addHeader("Set-Cookie", "SESSION=mock-session-id; Path=/; HttpOnly");
result.getResponse().addHeader("Set-Cookie", "SESSION=session-id; Path=/; HttpOnly");

// then
resultActions
Expand All @@ -149,7 +150,9 @@ private RestDocumentationResultHandler getDocument(
.andDo(print());

// documentation
resultActions.andDo(getDocument(2, "SESSION 쿠키: 소셜 회원가입이 필요한 사용자를 위한 임시 세션 (만료 시간 5분)"));
resultActions.andDo(
getDocument(
2, "소셜 유저 정보를 담은 임시 세션 쿠키 (만료 시간: 5분, , Value: SESSION=session-id 형태)"));
}

@Test
Expand Down
133 changes: 133 additions & 0 deletions src/test/java/com/ftm/server/auth/LogoutTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.ftm.server.auth;

import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.restdocs.headers.HeaderDocumentation.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.JsonFieldType.*;
import static org.springframework.restdocs.payload.JsonFieldType.STRING;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.ftm.server.BaseTest;
import com.ftm.server.application.dto.command.GeneralUserCreationCommand;
import com.ftm.server.application.port.AuthenticationPort;
import com.ftm.server.application.port.repository.UserImageRepository;
import com.ftm.server.application.port.repository.UserRepository;
import com.ftm.server.common.response.enums.ErrorResponseCode;
import com.ftm.server.domain.entity.User;
import com.ftm.server.domain.entity.UserImage;
import com.ftm.server.web.dto.request.UserLoginRequest;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.transaction.annotation.Transactional;

public class LogoutTest extends BaseTest {

@Autowired private UserRepository userRepository;

@Autowired private UserImageRepository userImageRepository;

@Autowired private AuthenticationPort authenticationPort;

private final List<FieldDescriptor> responseFieldLogout =
List.of(
fieldWithPath("status").type(NUMBER).description("응답 상태"),
fieldWithPath("code").type(STRING).description("상태 코드"),
fieldWithPath("message").type(STRING).description("메시지"),
fieldWithPath("data").type(OBJECT).optional().description("data"));

private RestDocumentationResultHandler getDocument(Integer identifier) {
return document(
"logout/" + identifier,
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint(), getModifiedHeader()),
requestHeaders(
headerWithName("Cookie")
.description("로그인 시 발급받은 SESSION Cookie")
.optional()),
responseFields(responseFieldLogout),
resource(
ResourceSnippetParameters.builder()
.tag("인증/인가")
.summary("로그아웃 api")
.description("로그아웃 api 입니다.")
.responseFields(responseFieldLogout)
.build()));
}

@BeforeEach
void setUp() {
GeneralUserCreationCommand command =
new GeneralUserCreationCommand(
"test@gmail.com",
authenticationPort.passwordEncode("test1234!"),
"test",
null,
null);
User testUser = User.createGeneralUser(command);
User saved = userRepository.save(testUser);
userImageRepository.save(UserImage.createUserImage(saved));
}

@Test
@Transactional
void 로그아웃_성공() throws Exception {
// given
UserLoginRequest request = new UserLoginRequest("test@gmail.com", "test1234!");

// when
MvcResult loginResult =
mockMvc.perform(
RestDocumentationRequestBuilders.post("/api/auth/login")
.contentType(APPLICATION_JSON_VALUE)
.content(mapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andReturn();
MockHttpSession session = (MockHttpSession) loginResult.getRequest().getSession(false);

ResultActions resultActions =
mockMvc.perform(
RestDocumentationRequestBuilders.post("/api/auth/logout").session(session));

// 요청 헤더에 세션 쿠키 수동 추가 (문서화 통과용)
MvcResult result = resultActions.andReturn();
result.getRequest().addHeader("Cookie", "SESSION=mock-session-id; Path=/; HttpOnly");

// then
resultActions.andExpect(status().isOk()).andDo(print());

// documentation
resultActions.andDo(getDocument(1));
}

@Test
@Transactional
void 로그아웃_실패() throws Exception {
// when
ResultActions resultActions =
mockMvc.perform(RestDocumentationRequestBuilders.post("/api/auth/logout"));

// then
resultActions
.andExpect(status().is(ErrorResponseCode.NOT_AUTHENTICATED.getHttpStatus().value()))
.andExpect(jsonPath("code").value(ErrorResponseCode.NOT_AUTHENTICATED.getCode()))
.andDo(print());

// documentation
resultActions.andDo(getDocument(2));
}
}
5 changes: 3 additions & 2 deletions src/test/java/com/ftm/server/auth/UserLoginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ private RestDocumentationResultHandler getDocument(Integer identifier) {
requestFields(requestFieldLoginUser),
responseHeaders(
headerWithName("Set-Cookie")
.description("세션 ID를 담고 있는 쿠키 (SESSION), 만료 시간: 1시간")
.description(
"로그인 성공 시 발급되는 세션 쿠키 (만료 시간: 1시간, Value: SESSION=session-id 형태)")
.optional()),
responseFields(responseFieldLoginUser),
resource(
Expand Down Expand Up @@ -120,7 +121,7 @@ void setUp() {
MvcResult result = resultActions.andReturn();

// 세션 쿠키 수동 추가 (문서화 통과용)
result.getResponse().addHeader("Set-Cookie", "SESSION=mock-session-id; Path=/; HttpOnly");
result.getResponse().addHeader("Set-Cookie", "SESSION=session-id; Path=/; HttpOnly");

// then
resultActions
Expand Down