Refresh Token의 문제점을 생각해봤다.
1) 유효기간이 긴 RT가 탈취될 경우 보안은...?
이 경우 Refresh Token Rotation을 구현하여 Refresh Token의 일회성을 보장하면 해결될 듯 하다.
그런데 문제는 다음부터다.
2) 탈취범이 정상유저보다 먼저 Refresh Token을 재발급 받으면🥲?
3) 그래서 한 명의 사용자에 여러 RT가 생기면🥲?
이 문제들에 대한 내 나름대로의 해결책을 정리해본다.
public Token refreshAccessToken(Refresh token) {
String reqRefreshToken = token.getRefreshToken();
ActiveGardener activeGardener = redisRepository.findById(token.getGardenerId())
.orElseThrow(() -> new BadCredentialsException(ExceptionCode.NO_TOKEN_IN_REDIS.getCode()));
RefreshToken savedRefreshToken = activeGardener.getRefreshToken();
if (!reqRefreshToken.equals(savedRefreshToken.getToken())) {
// redis의 refresh token과 일치하지 않음 -- B011
redisRepository.deleteById(token.getGardenerId());
throw new BadCredentialsException(ExceptionCode.INVALID_REFRESH_TOKEN.getCode());
} else if (savedRefreshToken.getExpiredAt().isBefore(LocalDateTime.now())) {
// refresh token 만료 -- B002
redisRepository.deleteById(token.getGardenerId());
throw new BadCredentialsException(ExceptionCode.REFRESH_TOKEN_EXPIRED.getCode());
}
// 새 access token 만들기
AccessToken accessToken = tokenProvider.createAccessToken(activeGardener.getGardenerId(), activeGardener.getName());
// Refresh Token Rotation
// Access token 재발급 시 Refresh Token도 재발급
RefreshToken newRefreshToken = new RefreshToken();
activeGardener.updateRefreshToken(newRefreshToken);
redisRepository.save(activeGardener);
return new Token(accessToken.getToken(), newRefreshToken.getToken());
}
구현 자체는 간단하다.
redis-cli를 통해 간단히 refresh token이 업데이트되는 걸 확인할 수 있다.
2) 탈취범이 정상유저보다 먼저 Refresh Token을 재발급 받으면🥲?
3) 그래서 한 명의 사용자에 여러 RT가 생기면🥲?
위의 문제는 Redis ID로 유저PK를 쓰고 있어서 (나름) 손쉽게 해결되었다.
구글링을 통해 본 코드는 대체로 RT를 key로 사용해 사용자 정보를 value로 저장해 그 정보를 기반으로 AT를 재발급했다. 원래는 redis를 도입한 만큼 UserDetailsService.loadByUsername()에서 빠르게 유저 정보를 가져오려고 바꿔서 설계했던건데, Redis Id를 유저 pk로 하는데에는 좀 더 많은 이점이 있었다.
Redis Id를 유저 pk로 잡으면, Redis 서버에서 사용자와 Refresh Token을 1:1로 매치하는 것을 강제할 수 있기 때문이다.
예상되는 흐름은 다음과 같다.
Id를 RT로 잡으면 Redis엔 하나의 유저에 여러개의 RT가 저장되고, 어느 것이 정상 유저의 RT인지 알 수 없다.
고민은 이렇게 했는데 코드는 몇 줄 없다.
어이는 더 없다...