diff --git a/src/main/java/com/Timo/Timo/global/auth/service/AuthService.java b/src/main/java/com/Timo/Timo/global/auth/service/AuthService.java index 6f1db3cb..227f569e 100644 --- a/src/main/java/com/Timo/Timo/global/auth/service/AuthService.java +++ b/src/main/java/com/Timo/Timo/global/auth/service/AuthService.java @@ -1,6 +1,5 @@ package com.Timo.Timo.global.auth.service; -import com.Timo.Timo.domain.calendar.client.GoogleOAuthClient; import com.Timo.Timo.domain.calendar.entity.CalendarRevocationOutbox; import com.Timo.Timo.domain.calendar.repository.CalendarConnectionRepository; import com.Timo.Timo.domain.calendar.repository.CalendarRevocationOutboxRepository; @@ -81,12 +80,10 @@ public ReissueResult reissue(String refreshToken, String sessionId) { throw new CustomException(UserErrorCode.USER_NOT_FOUND); } - if (!refreshTokenService.isRefreshTokenValid(String.valueOf(userId), sessionId, refreshToken)){ + if (!refreshTokenService.validateAndConsumeRefreshToken(String.valueOf(userId), sessionId, refreshToken)) { throw new CustomException(AuthErrorCode.INVALID_REFRESH_TOKEN); } - refreshTokenService.deleteRefreshToken(String.valueOf(userId), sessionId); - String newAccessToken = jwtTokenProvider.generateAccessToken(userId); String newRefreshToken = jwtTokenProvider.generateRefreshToken(userId); String newSessionId = refreshTokenService.saveRefreshToken(String.valueOf(userId), newRefreshToken); diff --git a/src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java b/src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java index ef704d23..e13cd022 100644 --- a/src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java +++ b/src/main/java/com/Timo/Timo/global/auth/service/RefreshTokenService.java @@ -3,14 +3,14 @@ import com.Timo.Timo.global.jwt.provider.JwtTokenProvider; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Service; @Service @@ -22,6 +22,17 @@ public class RefreshTokenService { private static final String KEY_PREFIX = "refresh:"; + private static final RedisScript COMPARE_AND_DELETE_SCRIPT = new DefaultRedisScript<>( + "local stored = redis.call('GET', KEYS[1]) " + + "if stored == ARGV[1] then " + + " redis.call('DEL', KEYS[1]) " + + " return 1 " + + "else " + + " return 0 " + + "end", + Long.class + ); + public String saveRefreshToken(String userId, String refreshToken){ String sessionId = UUID.randomUUID().toString(); redisTemplate.opsForValue().set( @@ -33,10 +44,6 @@ public String saveRefreshToken(String userId, String refreshToken){ return sessionId; } - public String getRefreshToken(String userId, String sessionId) { - return redisTemplate.opsForValue().get(KEY_PREFIX + userId + ":" + sessionId); - } - public void deleteRefreshToken(String userId, String sessionId) { redisTemplate.delete(KEY_PREFIX + userId + ":" + sessionId); } @@ -60,7 +67,12 @@ public void deleteAllRefreshTokens(String userId) { } } - public boolean isRefreshTokenValid(String userId, String sessionId, String refreshToken) { - return Objects.equals(refreshToken, getRefreshToken(userId, sessionId)); + public boolean validateAndConsumeRefreshToken(String userId, String sessionId, String refreshToken) { + Long result = redisTemplate.execute( + COMPARE_AND_DELETE_SCRIPT, + List.of(KEY_PREFIX + userId + ":" + sessionId), + refreshToken + ); + return Long.valueOf(1L).equals(result); } } diff --git a/src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java b/src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java index 8d2a2382..bd97d655 100644 --- a/src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java +++ b/src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java @@ -6,32 +6,22 @@ public class CookieUtil { public static ResponseCookie createCookie(String name, String value, long maxAgeSeconds, boolean secure) { - ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from(name, value) + return ResponseCookie.from(name, value) .httpOnly(true) .secure(secure) .path("/api/v1/auth") .maxAge(Duration.ofSeconds(maxAgeSeconds)) - .sameSite(secure ? "None" : "Strict"); - - if (secure) { - builder.partitioned(true); - } - - return builder.build(); + .sameSite(secure ? "None" : "Strict") + .build(); } public static ResponseCookie expireCookie(String name, boolean secure) { - ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from(name, "") + return ResponseCookie.from(name, "") .httpOnly(true) .secure(secure) .path("/api/v1/auth") .maxAge(0) - .sameSite(secure ? "None" : "Strict"); - - if (secure) { - builder.partitioned(true); - } - - return builder.build(); + .sameSite(secure ? "None" : "Strict") + .build(); } }