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
@@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -22,23 +22,30 @@ public class RefreshTokenService {

private static final String KEY_PREFIX = "refresh:";

private static final RedisScript<Long> 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(
KEY_PREFIX + userId + ":" + sessionId,
buildKey(userId, sessionId),
refreshToken,
jwtTokenProvider.getRefreshTokenExpiry(),
TimeUnit.SECONDS
);
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);
redisTemplate.delete(buildKey(userId, sessionId));
}

public void deleteAllRefreshTokens(String userId) {
Expand All @@ -60,7 +67,16 @@ 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(buildKey(userId, sessionId)),
refreshToken
);
return Long.valueOf(1L).equals(result);
}

private String buildKey(String userId, String sessionId) {
return KEY_PREFIX + userId + ":" + sessionId;
}
}
22 changes: 6 additions & 16 deletions src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Loading