Skip to content

Commit

Permalink
Fix/GH-137-refresh-token-reuse-bug (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
inh2613 authored Jul 23, 2023
2 parents a04ec10 + 2ae8ce5 commit bacbd4c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,17 @@ public TokenDto signIn(@RequestBody SignInDto loginDto) {
}

@PostMapping("/refresh")
@Operation(summary = "Refresh Token 재발급 메서드", description = "Refresh Token을 재발급 받기 위한 메서드입니다.")
public TokenDto validateRefreshToken(@RequestHeader("Authorization") String refreshToken) {
@Operation(summary = "Refresh Token을 이용한 Access Token 재발급 메서드", description = "Refresh Token을 이용하여 Access Token을 재발급 받기 위한 메서드입니다.")
public TokenDto reissueAccessToken(@RequestHeader("Authorization") String refreshToken) {
String newAccessToken = refreshTokenService.createNewAccessTokenByValidateRefreshToken(refreshToken);
String newRefreshToken = refreshTokenService.createNewRefreshTokenByValidateRefreshToken(refreshToken);

refreshToken = refreshToken.substring(7);

TokenDto tokenDto = TokenDto.builder()
.accessToken(newAccessToken)
.refreshToken(newRefreshToken)
.refreshToken(refreshToken)
.build();

refreshToken = refreshToken.substring(7);
refreshTokenService.storeRefreshToken(tokenDto, jwtProvider.getUsername(refreshToken));
return tokenDto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.swmaestro.repl.gifthub.auth.repository.RefreshTokenRepository;
import org.swmaestro.repl.gifthub.exception.BusinessException;
import org.swmaestro.repl.gifthub.exception.ErrorCode;
import org.swmaestro.repl.gifthub.security.JpaUserDetailsService;

import java.time.Instant;
Expand Down Expand Up @@ -135,7 +137,7 @@ public String reissueAccessToken(String refreshToken) {
String storedRefreshToken = refreshTokenRepository.findByUsername(username).get().getRefreshToken();

if (!refreshToken.equals(storedRefreshToken)) {
throw new IllegalArgumentException("유효하지 않은 토큰입니다.");
throw new BusinessException("RefreshToken이 유효하지 않습니다.", ErrorCode.INVALID_AUTHENTICATION);
}
return generateToken(username);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
package org.swmaestro.repl.gifthub.auth.controller;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.security.PrivateKey;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.swmaestro.repl.gifthub.auth.dto.GoogleDto;
import org.swmaestro.repl.gifthub.auth.dto.KakaoDto;
import org.swmaestro.repl.gifthub.auth.dto.SignInDto;
import org.swmaestro.repl.gifthub.auth.dto.SignUpDto;
import org.swmaestro.repl.gifthub.auth.dto.TokenDto;
import org.swmaestro.repl.gifthub.auth.dto.*;
import org.swmaestro.repl.gifthub.auth.entity.Member;
import org.swmaestro.repl.gifthub.auth.service.AppleService;
import org.swmaestro.repl.gifthub.auth.service.AuthService;
import org.swmaestro.repl.gifthub.auth.service.GoogleService;
import org.swmaestro.repl.gifthub.auth.service.KakaoService;
import org.swmaestro.repl.gifthub.auth.service.MemberService;
import org.swmaestro.repl.gifthub.auth.service.NaverService;
import org.swmaestro.repl.gifthub.auth.service.RefreshTokenService;
import org.swmaestro.repl.gifthub.auth.service.*;
import org.swmaestro.repl.gifthub.util.JwtProvider;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.security.PrivateKey;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
Expand Down Expand Up @@ -68,66 +59,64 @@ public class AuthControllerTest {
@Test
public void signUpTest() throws Exception {
SignUpDto signUpDto = SignUpDto.builder()
.username("jinlee1703")
.password("abc123##")
.nickname("이진우")
.build();
.username("jinlee1703")
.password("abc123##")
.nickname("이진우")
.build();

TokenDto tokenDto = TokenDto.builder()
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();

// when
when(memberService.create(signUpDto)).thenReturn(tokenDto);

// then
mockMvc.perform(post("/auth/sign-up")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(signUpDto)))
.andExpect(status().isOk());
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(signUpDto)))
.andExpect(status().isOk());
}

@Test
public void signInTest() throws Exception {
SignInDto loginDto = SignInDto.builder()
.username("jinlee1703")
.password("abc123##")
.build();
.username("jinlee1703")
.password("abc123##")
.build();

TokenDto tokenDto = TokenDto.builder()
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();

when(authService.signIn(any(SignInDto.class))).thenReturn(tokenDto);

mockMvc.perform(post("/auth/sign-up")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginDto)))
.andExpect(status().isOk());
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(loginDto)))
.andExpect(status().isOk());
}

@Test
public void validateRefreshTokenTest() throws Exception {
public void reissueAccessTokenTest() throws Exception {
//given
String refreshToken = "유효하지 않은 Refresh Token";
String refreshToken = "유효한 Refresh Token";
String newAccessToken = "sampleNewAccessToken";
String newRefreshToken = "sampleNewRefreshToken";
String username = "jinlee1703";

TokenDto tokenDto = TokenDto.builder()
.accessToken(newAccessToken)
.refreshToken(newRefreshToken)
.build();
.accessToken(newAccessToken)
.refreshToken(refreshToken)
.build();

when(refreshTokenService.createNewAccessTokenByValidateRefreshToken(refreshToken)).thenReturn(null);
when(refreshTokenService.createNewRefreshTokenByValidateRefreshToken(refreshToken)).thenReturn(null);
when(refreshTokenService.createNewAccessTokenByValidateRefreshToken(refreshToken)).thenReturn(newAccessToken);
when(jwtProvider.getUsername(refreshToken)).thenReturn(username);

mockMvc.perform(post("/auth/refresh")
.header("Authorization", refreshToken))
.andExpect(status().isUnauthorized());
.header("Authorization", refreshToken))
.andExpect(status().isUnauthorized());
}

@Test
Expand All @@ -137,29 +126,29 @@ public void kakaoSignInCallbackTest() throws Exception {
String state = "myawesome_state";

TokenDto kakaoTokenDto = TokenDto.builder()
.accessToken("myawesomeKakaojwt")
.refreshToken("myawesomeKakaojwt")
.build();
.accessToken("myawesomeKakaojwt")
.refreshToken("myawesomeKakaojwt")
.build();

TokenDto tokenDto = TokenDto.builder()
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();

KakaoDto kakaoDto = KakaoDto.builder()
.nickname("정인희")
.username("[email protected]")
.build();
.nickname("정인희")
.username("[email protected]")
.build();

when(kakaoService.getToken(code)).thenReturn(tokenDto);
when(kakaoService.getUserInfo(tokenDto)).thenReturn(kakaoDto);
when(kakaoService.signIn(kakaoDto)).thenReturn(tokenDto);

mockMvc.perform(get("/auth/sign-in/naver/callback")
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
}

@Test
Expand All @@ -169,29 +158,29 @@ public void googleSignInCallbackTest() throws Exception {
String state = "myawesome_state";

TokenDto googleTokenDto = TokenDto.builder()
.accessToken("myawesomeKakaojwt")
.refreshToken("myawesomeKakaojwt")
.build();
.accessToken("myawesomeKakaojwt")
.refreshToken("myawesomeKakaojwt")
.build();

TokenDto tokenDto = TokenDto.builder()
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();
.accessToken("myawesomejwt")
.refreshToken("myawesomejwt")
.build();

GoogleDto googleDto = GoogleDto.builder()
.nickname("정인희")
.username("[email protected]")
.build();
.nickname("정인희")
.username("[email protected]")
.build();

when(googleService.getToken(code)).thenReturn(tokenDto);
when(googleService.getUserInfo(tokenDto)).thenReturn(googleDto);
when(googleService.signIn(googleDto)).thenReturn(tokenDto);

mockMvc.perform(get("/auth/sign-in/naver/callback")
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
}

@Test
Expand All @@ -200,22 +189,22 @@ public void naverSignInCallbackTest() throws Exception {
String code = "myawesome_code";
String state = "myawesome_state";
TokenDto token = TokenDto.builder()
.accessToken(accesstoken)
.refreshToken(accesstoken)
.build();
.accessToken(accesstoken)
.refreshToken(accesstoken)
.build();
Member member = Member.builder()
.username("[email protected]")
.nickname("이진우")
.build();
.username("[email protected]")
.nickname("이진우")
.build();

when(naverService.getNaverToken("token", code)).thenReturn(token);
when(naverService.getNaverUserByToken(token)).thenReturn(member);

mockMvc.perform(get("/auth/sign-in/naver/callback")
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
.queryParam("code", code)
.queryParam("state", state)
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isOk());
}

@Test
Expand All @@ -228,9 +217,9 @@ public void appleSignInCallbackTest() throws Exception {
PrivateKey privateKey = mock(PrivateKey.class);

TokenDto token = TokenDto.builder()
.accessToken(accesstoken)
.refreshToken(refreshToken)
.build();
.accessToken(accesstoken)
.refreshToken(refreshToken)
.build();

when(appleService.readKeyPath()).thenReturn(keyPath);
when(appleService.craetePrivateKey(keyPath)).thenReturn(privateKey);
Expand All @@ -239,8 +228,8 @@ public void appleSignInCallbackTest() throws Exception {
when(appleService.getToken(idToken)).thenReturn(token);

mockMvc.perform(post("/auth/sign-in/apple/callback")
.requestAttr("code", "my_awesome_code")
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isBadRequest());
.requestAttr("code", "my_awesome_code")
.header("Authorization", "Bearer " + accesstoken))
.andExpect(status().isBadRequest());
}
}

0 comments on commit bacbd4c

Please sign in to comment.