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 @@ -97,19 +97,18 @@ public ResponseEntity<SignInResponse> signUp(
public ResponseEntity<Void> signOut(
Authentication authentication
) {
String accessToken = (String) authentication.getCredentials();
if (accessToken == null || accessToken.isBlank()) {
throw new CustomException(ErrorCode.AUTHENTICATION_FAILED, "토큰이 없습니다.");
}
String accessToken = getAccessToken(authentication);
authService.signOut(accessToken);
return ResponseEntity.ok().build();
}

@PatchMapping("/quit")
public ResponseEntity<Void> quit(
@AuthorizedUser SiteUser siteUser
@AuthorizedUser SiteUser siteUser,
Authentication authentication // todo: #299를 작업하며 인자를 (Authentication authentication)만 받도록 수정해야 함
) {
authService.quit(siteUser);
String accessToken = getAccessToken(authentication);
authService.quit(siteUser, accessToken);
return ResponseEntity.ok().build();
}

Expand All @@ -120,4 +119,11 @@ public ResponseEntity<ReissueResponse> reissueToken(
ReissueResponse reissueResponse = authService.reissue(reissueRequest);
return ResponseEntity.ok(reissueResponse);
}

private String getAccessToken(Authentication authentication) {
if (authentication == null || !(authentication.getCredentials() instanceof String accessToken)) {
throw new CustomException(ErrorCode.AUTHENTICATION_FAILED, "엑세스 토큰이 없습니다.");
}
return accessToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,27 @@ public class AuthService {
private final AuthTokenProvider authTokenProvider;

/*
* 로그아웃 한다.
* 로그아웃한다.
* - 엑세스 토큰을 블랙리스트에 추가한다.
* - 리프레시 토큰을 삭제한다.
* */
public void signOut(String token) {
AccessToken accessToken = authTokenProvider.toAccessToken(token);
authTokenProvider.deleteRefreshTokenByAccessToken(accessToken);
authTokenProvider.addToBlacklist(accessToken);
}

/*
* 탈퇴한다.
* - 탈퇴한 시점의 다음날을 탈퇴일로 잡는다.
* - e.g. 2024-01-01 18:00 탈퇴 시, 2024-01-02 00:00 가 탈퇴일이 된다.
* - 로그아웃한다.
* */
@Transactional
public void quit(SiteUser siteUser) {
public void quit(SiteUser siteUser, String token) { // todo: #299를 작업하며 인자를 (String token)만 받도록 수정해야 함
LocalDate tomorrow = LocalDate.now().plusDays(1);
siteUser.setQuitedAt(tomorrow);
signOut(token);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public boolean isValidRefreshToken(String requestedRefreshToken) {
return Objects.equals(requestedRefreshToken, foundRefreshToken);
}

public void deleteRefreshTokenByAccessToken(AccessToken accessToken) {
String subject = accessToken.subject().value();
String refreshTokenKey = TokenType.REFRESH.addPrefix(subject);
redisTemplate.delete(refreshTokenKey);
}

@Override
public boolean isTokenBlacklisted(String accessToken) {
String blackListTokenKey = TokenType.BLACKLIST.addPrefix(accessToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.solidconnection.auth.service;

import com.example.solidconnection.auth.domain.TokenType;
import com.example.solidconnection.auth.dto.ReissueRequest;
import com.example.solidconnection.auth.dto.ReissueResponse;
import com.example.solidconnection.custom.exception.CustomException;
Expand All @@ -12,12 +13,14 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.LocalDate;

import static com.example.solidconnection.custom.exception.ErrorCode.REFRESH_TOKEN_EXPIRED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.jupiter.api.Assertions.assertAll;

@DisplayName("인증 서비스 테스트")
@TestContainerSpringBootTest
Expand All @@ -32,29 +35,44 @@ class AuthServiceTest {
@Autowired
private SiteUserRepository siteUserRepository;

@Autowired
private RedisTemplate<String, String> redisTemplate;

@Test
void 로그아웃한다() {
// given
AccessToken accessToken = authTokenProvider.generateAccessToken(new Subject("subject")); // todo: #296
Subject subject = new Subject("subject");
AccessToken accessToken = authTokenProvider.generateAccessToken(subject); // todo: #296

// when
authService.signOut(accessToken.token());

// then
assertThat(authTokenProvider.isTokenBlacklisted(accessToken.token())).isTrue();
String refreshTokenKey = TokenType.REFRESH.addPrefix(subject.value());
assertAll(
() -> assertThat(redisTemplate.opsForValue().get(refreshTokenKey)).isNull(),
() -> assertThat(authTokenProvider.isTokenBlacklisted(accessToken.token())).isTrue()
);
}

@Test
void 탈퇴한다() {
// given
SiteUser siteUser = createSiteUser();
Subject subject = authTokenProvider.toSubject(siteUser);
AccessToken accessToken = authTokenProvider.generateAccessToken(subject); // todo: #296

// when
authService.quit(siteUser);
authService.quit(siteUser, accessToken.token());

// then
LocalDate tomorrow = LocalDate.now().plusDays(1);
assertThat(siteUser.getQuitedAt()).isEqualTo(tomorrow);
String refreshTokenKey = TokenType.REFRESH.addPrefix(subject.value());
assertAll(
() -> assertThat(siteUser.getQuitedAt()).isEqualTo(tomorrow),
() -> assertThat(redisTemplate.opsForValue().get(refreshTokenKey)).isNull(),
() -> assertThat(authTokenProvider.isTokenBlacklisted(accessToken.token())).isTrue()
);
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ class 리프레시_토큰을_제공한다 {
() -> assertThat(authTokenProvider.isValidRefreshToken(fakeRefreshToken.token())).isFalse()
);
}

@Test
void 액세서_토큰에_해당하는_리프레시_토큰을_삭제한다() {
// given
authTokenProvider.generateAndSaveRefreshToken(subject);
AccessToken accessToken = authTokenProvider.generateAccessToken(subject);

// when
authTokenProvider.deleteRefreshTokenByAccessToken(accessToken);

// then
String refreshTokenKey = TokenType.REFRESH.addPrefix(subject.value());
assertThat(redisTemplate.opsForValue().get(refreshTokenKey)).isNull();
}
}

@Nested
Expand Down
Loading