Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
6 changes: 4 additions & 2 deletions src/main/java/com/mycom/socket/auth/config/JWTProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
@ConfigurationProperties(prefix = "jwt")
public class JWTProperties {
private String secret;
private long accessTokenValidityInSeconds = 1800;
private String cookieName = "Authorization";
private long accessTokenValidityInSeconds;
private long refreshTokenValidityInSeconds;
private String accessTokenCookieName;
private String refreshTokenCookieName;
Comment on lines +13 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

ν•„μˆ˜ 속성에 λŒ€ν•œ μœ νš¨μ„± 검증 μΆ”κ°€ ν•„μš”

토큰 유효 κΈ°κ°„κ³Ό μΏ ν‚€ 이름은 ν•„μˆ˜ κ°’μ΄λ―€λ‘œ, 이에 λŒ€ν•œ μœ νš¨μ„± 검증이 ν•„μš”ν•©λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•˜λŠ” 것을 μ œμ•ˆν•©λ‹ˆλ‹€:

+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Positive;

 @Getter
 @Setter
 @Component
 @ConfigurationProperties(prefix = "jwt")
 public class JWTProperties {
     private String secret;
-    private long accessTokenValidityInSeconds;
-    private long refreshTokenValidityInSeconds;
-    private String accessTokenCookieName;
-    private String refreshTokenCookieName;
+    @Positive(message = "μ•‘μ„ΈμŠ€ 토큰 유효 기간은 μ–‘μˆ˜μ—¬μ•Ό ν•©λ‹ˆλ‹€")
+    private long accessTokenValidityInSeconds;
+    @Positive(message = "λ¦¬ν”„λ ˆμ‹œ 토큰 유효 기간은 μ–‘μˆ˜μ—¬μ•Ό ν•©λ‹ˆλ‹€")
+    private long refreshTokenValidityInSeconds;
+    @NotBlank(message = "μ•‘μ„ΈμŠ€ 토큰 μΏ ν‚€ 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€")
+    private String accessTokenCookieName;
+    @NotBlank(message = "λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€")
+    private String refreshTokenCookieName;
     private String issuer = "go_socket";
     private boolean secureCookie = false;
 }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private long accessTokenValidityInSeconds;
private long refreshTokenValidityInSeconds;
private String accessTokenCookieName;
private String refreshTokenCookieName;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "jwt")
public class JWTProperties {
private String secret;
@Positive(message = "μ•‘μ„ΈμŠ€ 토큰 유효 기간은 μ–‘μˆ˜μ—¬μ•Ό ν•©λ‹ˆλ‹€")
private long accessTokenValidityInSeconds;
@Positive(message = "λ¦¬ν”„λ ˆμ‹œ 토큰 유효 기간은 μ–‘μˆ˜μ—¬μ•Ό ν•©λ‹ˆλ‹€")
private long refreshTokenValidityInSeconds;
@NotBlank(message = "μ•‘μ„ΈμŠ€ 토큰 μΏ ν‚€ 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€")
private String accessTokenCookieName;
@NotBlank(message = "λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€")
private String refreshTokenCookieName;
private String issuer = "go_socket";
private boolean secureCookie = false;
}

private String issuer = "go_socket";
private boolean secureCookie = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.mycom.socket.auth.controller;

import com.mycom.socket.auth.config.JWTProperties;
import com.mycom.socket.auth.dto.response.TokenResponse;
import com.mycom.socket.auth.jwt.JWTUtil;
import com.mycom.socket.auth.security.CookieUtil;
import com.mycom.socket.global.exception.BadRequestException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class RefreshController {

private final JWTUtil jwtUtil;
private final CookieUtil cookieUtil;
private final JWTProperties jwtProperties;

@PostMapping("/refresh")
public TokenResponse refreshAccessToken(HttpServletRequest request, HttpServletResponse response) {
String refreshToken = extractRefreshToken(request);

try {
jwtUtil.validateToken(refreshToken);
} catch (JwtException e) {
response.addCookie(cookieUtil.createExpiredRefreshCookie());
throw new BadRequestException("μœ νš¨ν•˜μ§€ μ•Šμ€ λ¦¬ν”„λ ˆμ‹œ ν† ν°μž…λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ£Όμ„Έμš”.");
}

String email = jwtUtil.getEmail(refreshToken);
String newAccessToken = jwtUtil.createToken(email, jwtProperties.getAccessTokenValidityInSeconds());

Cookie accessTokenCookie = cookieUtil.createAuthCookie(newAccessToken);
response.addCookie(accessTokenCookie);

String newRefreshToken = jwtUtil.createToken(email, jwtProperties.getRefreshTokenValidityInSeconds());
Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(newRefreshToken);
response.addCookie(refreshTokenCookie);

Comment on lines +41 to +47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codebase verification

토큰 관리 λ³΄μ•ˆ 취약점 확인됨

ν˜„μž¬ κ΅¬ν˜„μ—μ„œ λ‹€μŒκ³Ό 같은 λ³΄μ•ˆ 취약점이 λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€:

  • 이전 λ¦¬ν”„λ ˆμ‹œ 토큰이 λ¬΄νš¨ν™”λ˜μ§€ μ•Šμ•„ μž¬μ‚¬μš© κ°€λŠ₯
  • Redisκ°€ κ΅¬μ„±λ˜μ–΄ μžˆμœΌλ‚˜ 토큰 관리에 ν™œμš©λ˜μ§€ μ•ŠμŒ
  • λ™μ‹œ 토큰 κ°±μ‹  μš”μ²­μ— λŒ€ν•œ 처리 둜직 λΆ€μž¬

ꢌμž₯ 사항:

  1. Redisλ₯Ό ν™œμš©ν•œ 토큰 λΈ”λž™λ¦¬μŠ€νŠΈ κ΅¬ν˜„
  2. λ¦¬ν”„λ ˆμ‹œ 토큰 μž¬λ°œκΈ‰ μ‹œ 이전 토큰 μ¦‰μ‹œ λ¬΄νš¨ν™”
  3. 토큰 λ°œκΈ‰ 이λ ₯ 좔적을 μœ„ν•œ μ €μž₯μ†Œ κ΅¬ν˜„
  4. λ™μ‹œμ„± μ œμ–΄λ₯Ό μœ„ν•œ 락(Lock) λ©”μ»€λ‹ˆμ¦˜ λ„μž…
πŸ”— Analysis chain

토큰 μž¬λ°œκΈ‰ 둜직 검증 ν•„μš”

μƒˆλ‘œμš΄ 토큰 λ°œκΈ‰ μ‹œ λ‹€μŒ 사항듀을 확인해야 ν•©λ‹ˆλ‹€:

  1. 이전 λ¦¬ν”„λ ˆμ‹œ 토큰 λ¬΄νš¨ν™”
  2. 토큰 λ°œκΈ‰ 이λ ₯ 좔적
  3. λ™μ‹œ 토큰 κ°±μ‹  μš”μ²­ 처리
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for token revocation implementation
rg -l "TokenRevocationService|BlacklistService|InvalidatedTokenRepository"

Length of output: 76


Script:

#!/bin/bash
# Search for token-related implementations with broader patterns
rg -l "token.*repository|token.*service|token.*cache|token.*store"

# Search for security configurations
rg -l "SecurityConfig|WebSecurityConfig|JwtConfig"

# Search for Redis or cache configurations
rg -l "RedisConfig|CacheConfig|TokenCache"

Length of output: 364


Script:

#!/bin/bash
# Check Redis configuration implementation
cat src/main/java/com/mycom/socket/global/config/RedisConfig.java

# Check security configuration for token handling
cat src/main/java/com/mycom/socket/auth/config/SecurityConfig.java

# Search for Redis usage in token operations
rg -g "!*.{test,spec}.*" "redisTemplate|StringRedisTemplate" -A 3

Length of output: 7750

return TokenResponse.of(newAccessToken);
}
Comment on lines +27 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

λ³΄μ•ˆ κ°œμ„ μ΄ ν•„μš”ν•©λ‹ˆλ‹€.

λ‹€μŒ λ³΄μ•ˆ κ΄€λ ¨ 사항듀을 κ³ λ €ν•΄μ£Όμ„Έμš”:

  1. CSRF λ³΄ν˜Έκ°€ ν•„μš”ν•©λ‹ˆλ‹€. @CrossOrigin μ„€μ •μ΄λ‚˜ CSRF 토큰 검증을 μΆ”κ°€ν•˜μ„Έμš”.
  2. 토큰 μž¬λ°œκΈ‰ μ—”λ“œν¬μΈνŠΈμ— λŒ€ν•œ rate limiting이 ν•„μš”ν•©λ‹ˆλ‹€.
  3. 토큰 μž¬λ°œκΈ‰ μ‹œλ„ μ‹€νŒ¨μ— λŒ€ν•œ 감사(audit) λ‘œκΉ…μ΄ ν•„μš”ν•©λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 rate limiting을 μΆ”κ°€ν•˜λŠ” 것을 μ œμ•ˆν•©λ‹ˆλ‹€:

+    @RateLimiter(name = "refreshToken", fallbackMethod = "refreshTokenFallback")
     @PostMapping("/refresh")
     public TokenResponse refreshAccessToken(...)

그리고 μ‹€νŒ¨ν•œ μ‹œλ„μ— λŒ€ν•œ λ‘œκΉ…μ„ μΆ”κ°€ν•˜μ„Έμš”:

     } catch (JwtException e) {
+        log.warn("Failed refresh token attempt for IP: {}", request.getRemoteAddr());
         response.addCookie(cookieUtil.createExpiredCookie(jwtProperties.getRefreshTokenCookieName()));
         throw new BadRequestException("μœ νš¨ν•˜μ§€ μ•Šμ€ λ¦¬ν”„λ ˆμ‹œ ν† ν°μž…λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ£Όμ„Έμš”.");
     }

Committable suggestion skipped: line range outside the PR's diff.


private String extractRefreshToken(HttpServletRequest request) {
if (request.getCookies() == null) {
throw new BadRequestException("λ¦¬ν”„λ ˆμ‹œ 토큰이 μ—†μŠ΅λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ£Όμ„Έμš”.");
}

for (Cookie cookie : request.getCookies()) {
if (jwtProperties.getRefreshTokenCookieName().equals(cookie.getName())) {
return cookie.getValue();
}
}
throw new BadRequestException("λ¦¬ν”„λ ˆμ‹œ 토큰이 μ—†μŠ΅λ‹ˆλ‹€. λ‹€μ‹œ λ‘œκ·ΈμΈν•΄μ£Όμ„Έμš”.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mycom.socket.auth.dto.response;

public record TokenResponse(
String accessToken
) {
public static TokenResponse of(String accessToken) {
return new TokenResponse(accessToken);
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/mycom/socket/auth/jwt/JWTFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private String resolveTokenFromCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (jwtProperties.getCookieName().equals(cookie.getName())) {
if (jwtProperties.getAccessTokenCookieName().equals(cookie.getName())) {
return cookie.getValue();
}
}
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/mycom/socket/auth/jwt/JWTUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,26 @@ public JWTUtil(JWTProperties jwtProperties) {
);
}

/**
* μ•‘μ„ΈμŠ€ 토큰 생성
*/
public String createAccessToken(String email) {
return createToken(email, jwtProperties.getAccessTokenValidityInSeconds());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

토큰 νƒ€μž… ꡬ뢄을 μœ„ν•œ claim μΆ”κ°€ ν•„μš”

μ•‘μ„ΈμŠ€ 토큰과 λ¦¬ν”„λ ˆμ‹œ 토큰을 ꡬ뢄할 수 μžˆλŠ” claim이 μ—†μ–΄ 토큰 μœ ν˜•μ„ 확인할 수 μ—†μŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•˜λŠ” 것을 μ œμ•ˆν•©λ‹ˆλ‹€:

     public String createAccessToken(String email) {
-        return createToken(email, jwtProperties.getAccessTokenValidityInSeconds());
+        return createToken(email, "ACCESS", jwtProperties.getAccessTokenValidityInSeconds());
     }

     public String createRefreshToken(String email) {
-        return createToken(email, jwtProperties.getRefreshTokenValidityInSeconds());
+        return createToken(email, "REFRESH", jwtProperties.getRefreshTokenValidityInSeconds());
     }

Also applies to: 38-40


/**
* λ¦¬ν”„λ ˆμ‹œ 토큰 생성
*/
public String createRefreshToken(String email) {
return createToken(email, jwtProperties.getRefreshTokenValidityInSeconds());
}

/**
* JWT 토큰 생성
*/
public String createToken(String email) {
public String createToken(String email, long validityInSeconds) {
Date now = new Date();
Date validity = new Date(now.getTime() +
(jwtProperties.getAccessTokenValidityInSeconds() * 1000));
Date validity = new Date(now.getTime() + (validityInSeconds * 1000));

return Jwts.builder()
.issuer(jwtProperties.getIssuer())
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/com/mycom/socket/auth/security/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,44 @@ public class CookieUtil {
* 인증 μΏ ν‚€ 생성
*/
public Cookie createAuthCookie(String token) {
Cookie cookie = new Cookie(jwtProperties.getCookieName(), token);
Cookie cookie = new Cookie(jwtProperties.getAccessTokenCookieName(), token);
cookie.setHttpOnly(true);
cookie.setSecure(jwtProperties.isSecureCookie());
cookie.setPath("/");
cookie.setMaxAge((int) jwtProperties.getAccessTokenValidityInSeconds());
return cookie;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

μΏ ν‚€ 생성 둜직 쀑볡 제거 ν•„μš”

인증 쿠킀와 λ¦¬ν”„λ ˆμ‹œ μΏ ν‚€ 생성 둜직이 μ€‘λ³΅λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 곡톡 λ©”μ„œλ“œλ‘œ μΆ”μΆœν•˜λ©΄ μ½”λ“œ μœ μ§€λ³΄μˆ˜κ°€ 더 μ‰¬μ›Œμ§ˆ 것 κ°™μŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•˜λŠ” 것을 μ œμ•ˆν•©λ‹ˆλ‹€:

+    private Cookie createCookie(String name, String value, long maxAgeInSeconds) {
+        Cookie cookie = new Cookie(name, value);
+        cookie.setHttpOnly(true);
+        cookie.setSecure(jwtProperties.isSecureCookie());
+        cookie.setPath("/");
+        cookie.setMaxAge((int) maxAgeInSeconds);
+        return cookie;
+    }

     public Cookie createAuthCookie(String token) {
-        Cookie cookie = new Cookie(jwtProperties.getAccessTokenCookieName(), token);
-        cookie.setHttpOnly(true);
-        cookie.setSecure(jwtProperties.isSecureCookie());
-        cookie.setPath("/");
-        cookie.setMaxAge((int) jwtProperties.getAccessTokenValidityInSeconds());
-        return cookie;
+        return createCookie(
+            jwtProperties.getAccessTokenCookieName(),
+            token,
+            jwtProperties.getAccessTokenValidityInSeconds()
+        );
     }

     public Cookie createRefreshCookie(String token) {
-        Cookie cookie = new Cookie(jwtProperties.getRefreshTokenCookieName(), token);
-        cookie.setHttpOnly(true);
-        cookie.setSecure(jwtProperties.isSecureCookie());
-        cookie.setPath("/");
-        cookie.setMaxAge((int) jwtProperties.getRefreshTokenValidityInSeconds());
-        return cookie;
+        return createCookie(
+            jwtProperties.getRefreshTokenCookieName(),
+            token,
+            jwtProperties.getRefreshTokenValidityInSeconds()
+        );
     }

Also applies to: 29-34

}
Comment on lines +23 to 31
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

λ³΄μ•ˆ κ°•ν™”λ₯Ό μœ„ν•œ SameSite 속성 μΆ”κ°€ ν•„μš”

μΏ ν‚€ 생성 μ‹œ CSRF 곡격 λ°©μ§€λ₯Ό μœ„ν•΄ SameSite 속성을 μΆ”κ°€ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

    private Cookie createCookie(String name, String value, long maxAge, boolean secure) {
        Cookie cookie = new Cookie(name, value);
        cookie.setHttpOnly(true);
        cookie.setSecure(secure);
        cookie.setPath("/");
        cookie.setMaxAge((int) maxAge);
+       cookie.setAttribute("SameSite", "Strict");
        return cookie;
    }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private Cookie createCookie(String name, String value, long maxAge, boolean secure) {
Cookie cookie = new Cookie(name, value);
cookie.setHttpOnly(true);
cookie.setSecure(jwtProperties.isSecureCookie());
cookie.setSecure(secure);
cookie.setPath("/");
cookie.setMaxAge((int) jwtProperties.getAccessTokenValidityInSeconds());
cookie.setMaxAge((int) maxAge);
return cookie;
}
private Cookie createCookie(String name, String value, long maxAge, boolean secure) {
Cookie cookie = new Cookie(name, value);
cookie.setHttpOnly(true);
cookie.setSecure(secure);
cookie.setPath("/");
cookie.setMaxAge((int) maxAge);
cookie.setAttribute("SameSite", "Strict");
return cookie;
}


/**
* λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 생성
*/
public Cookie createRefreshCookie(String token) {
Cookie cookie = new Cookie(jwtProperties.getRefreshTokenCookieName(), token);
cookie.setHttpOnly(true);
cookie.setSecure(jwtProperties.isSecureCookie());
cookie.setPath("/");
cookie.setMaxAge((int) jwtProperties.getRefreshTokenValidityInSeconds());
return cookie;
}


/**
* 인증 μΏ ν‚€ 만료 처리
*/
public Cookie createExpiredAuthCookie() {
Cookie cookie = new Cookie(jwtProperties.getCookieName(), null);
Cookie cookie = new Cookie(jwtProperties.getAccessTokenCookieName(), null);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(0); // μ¦‰μ‹œ 만료
return cookie;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

⚠️ Potential issue

만료된 μΏ ν‚€ 생성 둜직 쀑볡 제거 및 λ³΄μ•ˆ μ„€μ • κ°•ν™” ν•„μš”

만료된 μΏ ν‚€ 생성 λ‘œμ§λ„ μ€‘λ³΅λ˜μ–΄ 있으며, SameSite 속성 섀정이 λˆ„λ½λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•˜λŠ” 것을 μ œμ•ˆν•©λ‹ˆλ‹€:

+    private Cookie createExpiredCookie(String name) {
+        Cookie cookie = new Cookie(name, null);
+        cookie.setHttpOnly(true);
+        cookie.setSecure(true);
+        cookie.setPath("/");
+        cookie.setMaxAge(0);
+        cookie.setAttribute("SameSite", "Strict");
+        return cookie;
+    }

     public Cookie createExpiredAuthCookie() {
-        Cookie cookie = new Cookie(jwtProperties.getAccessTokenCookieName(), null);
-        cookie.setHttpOnly(true);
-        cookie.setSecure(true);
-        cookie.setPath("/");
-        cookie.setMaxAge(0);
-        return cookie;
+        return createExpiredCookie(jwtProperties.getAccessTokenCookieName());
     }

     public Cookie createExpiredRefreshCookie() {
-        Cookie cookie = new Cookie(jwtProperties.getRefreshTokenCookieName(), null);
-        cookie.setHttpOnly(true);
-        cookie.setSecure(true);
-        cookie.setPath("/");
-        cookie.setMaxAge(0);
-        return cookie;
+        return createExpiredCookie(jwtProperties.getRefreshTokenCookieName());
     }

Also applies to: 54-59

Comment on lines +49 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codebase verification

토큰 μœ νš¨κΈ°κ°„ 검증 둜직 κ΅¬ν˜„ λ°©μ•ˆ

λ¦¬ν”„λ ˆμ‹œ ν† ν°μ˜ 유효 기간이 μ•‘μ„ΈμŠ€ 토큰보닀 길도둝 보μž₯ν•˜κΈ° μœ„ν•΄ λ‹€μŒκ³Ό 같은 검증 둜직 μΆ”κ°€λ₯Ό μ œμ•ˆλ“œλ¦½λ‹ˆλ‹€:

  • @PostConstructλ₯Ό μ‚¬μš©ν•˜μ—¬ μ΄ˆκΈ°ν™” μ‹œμ μ— 토큰 μœ νš¨κΈ°κ°„μ„ 검증
  • Bean Validation을 ν™œμš©ν•˜μ—¬ 각 ν•„λ“œμ˜ μ΅œμ†Œκ°’ 검증 (@Min)
  • refreshTokenValidityInSecondsκ°€ accessTokenValidityInSeconds보닀 큰지 ν™•μΈν•˜λŠ” μ»€μŠ€ν…€ μœ νš¨μ„± 검사 μΆ”κ°€
πŸ”— Analysis chain

λ¦¬ν”„λ ˆμ‹œ 토큰 유효 κΈ°κ°„ 검증 μΆ”κ°€ κ³ λ €

λ¦¬ν”„λ ˆμ‹œ ν† ν°μ˜ 유효 기간이 μ•‘μ„ΈμŠ€ 토큰보닀 길게 μ„€μ •λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜λŠ” 검증 둜직 μΆ”κ°€λ₯Ό κ³ λ €ν•΄λ³΄μ„Έμš”.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# JWTPropertiesμ—μ„œ 토큰 유효 κΈ°κ°„ 섀정을 확인
rg "tokenValidityInSeconds|refreshTokenValidityInSeconds" -A 2

Length of output: 383


Script:

#!/bin/bash
# Get complete JWTProperties class
cat src/main/java/com/mycom/socket/auth/config/JWTProperties.java

# Check application properties/yaml files
fd -e yml -e yaml -e properties --exec cat {}

Length of output: 1162


/**
* λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 만료 처리
*/
public Cookie createExpiredRefreshCookie() {
Cookie cookie = new Cookie(jwtProperties.getRefreshTokenCookieName(), null);
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/com/mycom/socket/auth/security/LoginFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
MemberDetails memberDetails = (MemberDetails) authResult.getPrincipal();
Member member = memberDetails.getMember();

String token = jwtUtil.createToken(member.getEmail());
String accessToken = jwtUtil.createAccessToken(member.getEmail()); //μ•‘μ„ΈμŠ€ 토큰 생성
String refreshToken = jwtUtil.createRefreshToken(member.getEmail()); //λ¦¬ν”„λ ˆμ‹œ 토큰 생성

// μΏ ν‚€ 생성 및 μ„€μ •
Cookie authCookie = cookieUtil.createAuthCookie(token);
response.addCookie(authCookie);
Cookie accessTokenCookie = cookieUtil.createAuthCookie(accessToken); //μ•‘μ„ΈμŠ€ 토큰 μΏ ν‚€
Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(refreshToken); //λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€
response.addCookie(accessTokenCookie);
response.addCookie(refreshTokenCookie);
Comment on lines +69 to +72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codebase verification

μΏ ν‚€ λ³΄μ•ˆ 섀정이 λΆˆμ™„μ „ν•©λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 λ³΄μ•ˆ λ¬Έμ œκ°€ λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€:

  • SameSite 속성이 μ„€μ •λ˜μ–΄ μžˆμ§€ μ•Šμ•„ CSRF 곡격에 μ·¨μ•½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • Pathκ°€ "/"둜 μ„€μ •λ˜μ–΄ μžˆμ–΄ ν•„μš” μ΄μƒμœΌλ‘œ 넓은 λ²”μœ„μ— μΏ ν‚€κ°€ λ…ΈμΆœλ©λ‹ˆλ‹€.

μˆ˜μ •μ΄ ν•„μš”ν•œ 파일:

  • src/main/java/com/mycom/socket/auth/security/CookieUtil.java

ꢌμž₯ 사항:

  1. SameSite=Strict λ˜λŠ” Lax 속성 μΆ”κ°€
  2. μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ‹€μ œ μš”κ΅¬μ‚¬ν•­μ— 맞게 Path λ²”μœ„ μΆ•μ†Œ
  3. 운영 ν™˜κ²½μ—μ„œ Secure ν”Œλž˜κ·Έκ°€ λ°˜λ“œμ‹œ ν™œμ„±ν™”λ˜λ„λ‘ μ„€μ • κ²€ν† 
πŸ”— Analysis chain

μΏ ν‚€ λ³΄μ•ˆ 섀정을 ν™•μΈν•΄μ£Όμ„Έμš”.

μΏ ν‚€ 생성 μ‹œ λ‹€μŒ λ³΄μ•ˆ 속성듀이 μ˜¬λ°”λ₯΄κ²Œ μ„€μ •λ˜μ–΄ μžˆλŠ”μ§€ 확인이 ν•„μš”ν•©λ‹ˆλ‹€:

  • HttpOnly
  • Secure
  • SameSite
  • Path
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# CookieUtil의 κ΅¬ν˜„μ„ ν™•μΈν•˜μ—¬ λ³΄μ•ˆ μ„€μ • 검증
rg -A 10 "createAuthCookie|createRefreshCookie" --type java

Length of output: 8548


Script:

#!/bin/bash
# Check createCookie implementation and JwtProperties
echo "=== CookieUtil.createCookie implementation ==="
ast-grep --pattern 'createCookie($$$) { $$$ }'

echo -e "\n=== JwtProperties configuration ==="
rg -A 5 "class JwtProperties" --type java

Length of output: 282


Script:

#!/bin/bash
# μΏ ν‚€ 생성 λ©”μ†Œλ“œμ™€ μ„€μ • 파일 검색
echo "=== Cookie creation implementation ==="
rg -A 15 "private.*createCookie" --type java

echo -e "\n=== Security and JWT configuration ==="
fd -e yml -e properties -e yaml | xargs rg -l "jwt|cookie|secure"

Length of output: 1755


// 둜그인 응닡 생성
LoginResponse loginResponse = new LoginResponse(member.getEmail(), member.getNickname());
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/com/mycom/socket/auth/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mycom.socket.auth.service;

import com.mycom.socket.auth.config.JWTProperties;
import com.mycom.socket.auth.dto.response.RegisterResponse;
import com.mycom.socket.auth.jwt.JWTUtil;
import com.mycom.socket.auth.security.CookieUtil;
Expand Down Expand Up @@ -28,6 +29,7 @@ public class AuthService {
private final JWTUtil jwtUtil;
private final MailService mailService;
private final CookieUtil cookieUtil;
private final JWTProperties jwtProperties;

/**
* μ‚¬μš©μž 둜그인 처리
Expand All @@ -46,8 +48,17 @@ public LoginResponse login(LoginRequest request, HttpServletResponse response) {
throw new BadRequestException("잘λͺ»λœ λΉ„λ°€λ²ˆν˜Έμž…λ‹ˆλ‹€.");
}

String token = jwtUtil.createToken(member.getEmail());
response.addCookie(cookieUtil.createAuthCookie(token)); // CookieUtil μ‚¬μš©
String refreshToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getRefreshTokenValidityInSeconds());

Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(refreshToken);
response.addCookie(refreshTokenCookie);

String accessToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getAccessTokenValidityInSeconds());

Cookie accessTokenCookie = cookieUtil.createAuthCookie(accessToken);
response.addCookie(accessTokenCookie);
Comment on lines +51 to +65
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

μ‹¬κ°ν•œ 토큰 생성 둜직 였λ₯˜κ°€ λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같은 치λͺ…적인 λ¬Έμ œμ λ“€μ΄ μžˆμŠ΅λ‹ˆλ‹€:

  1. μ•‘μ„ΈμŠ€ 토큰이 λ¦¬ν”„λ ˆμ‹œ ν† ν°μ˜ 유효 기간을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€
  2. λ¦¬ν”„λ ˆμ‹œ 토큰이 μ•‘μ„ΈμŠ€ ν† ν°μ˜ 유효 기간을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€
  3. μΏ ν‚€ 생성 μ‹œ 토큰이 잘λͺ» μ „λ‹¬λ˜κ³  μžˆμŠ΅λ‹ˆλ‹€
  4. 토큰 νƒ€μž…μ΄ 잘λͺ» μ§€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€

λ‹€μŒκ³Ό 같이 μˆ˜μ •ν•΄μ£Όμ„Έμš”:

-        // λ¨Όμ € μ•‘μ„ΈμŠ€ 토큰 생성
-        String accessToken = jwtUtil.createToken(member.getEmail(),
-                jwtProperties.getRefreshTokenValidityInSeconds(), "ACCESS_TOKEN");
-
-        Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(accessToken);
-
-        // κ·Έ λ‹€μŒ λ¦¬ν”„λ ˆμ‹œ 토큰 생성
-        String refreshToken = jwtUtil.createToken(member.getEmail(),
-                jwtProperties.getAccessTokenValidityInSeconds(), "REFRESH_TOKEN");
-
-        Cookie accessTokenCookie = cookieUtil.createAuthCookie(refreshToken);
-
-        // μΏ ν‚€ μ„€μ •
-        response.addCookie(refreshTokenCookie);
-        response.addCookie(accessTokenCookie);
+        // μ•‘μ„ΈμŠ€ 토큰 생성
+        String accessToken = jwtUtil.createToken(member.getEmail(),
+                jwtProperties.getAccessTokenValidityInSeconds(), "ACCESS_TOKEN");
+        Cookie accessTokenCookie = cookieUtil.createAuthCookie(accessToken);
+
+        // λ¦¬ν”„λ ˆμ‹œ 토큰 생성
+        String refreshToken = jwtUtil.createToken(member.getEmail(),
+                jwtProperties.getRefreshTokenValidityInSeconds(), "REFRESH_TOKEN");
+        Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(refreshToken);
+
+        // μΏ ν‚€ μ„€μ •
+        response.addCookie(accessTokenCookie);
+        response.addCookie(refreshTokenCookie);
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// // λ¨Όμ € μ•‘μ„ΈμŠ€ 토큰 생성
String accessToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getRefreshTokenValidityInSeconds(), "ACCESS_TOKEN");
Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(accessToken);
// κ·Έ λ‹€μŒ λ¦¬ν”„λ ˆμ‹œ 토큰 생성
String refreshToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getAccessTokenValidityInSeconds(), "REFRESH_TOKEN");
Cookie accessTokenCookie = cookieUtil.createAuthCookie(refreshToken);
// μΏ ν‚€ μ„€μ •
response.addCookie(refreshTokenCookie);
response.addCookie(accessTokenCookie);
// μ•‘μ„ΈμŠ€ 토큰 생성
String accessToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getAccessTokenValidityInSeconds(), "ACCESS_TOKEN");
Cookie accessTokenCookie = cookieUtil.createAuthCookie(accessToken);
// λ¦¬ν”„λ ˆμ‹œ 토큰 생성
String refreshToken = jwtUtil.createToken(member.getEmail(),
jwtProperties.getRefreshTokenValidityInSeconds(), "REFRESH_TOKEN");
Cookie refreshTokenCookie = cookieUtil.createRefreshCookie(refreshToken);
// μΏ ν‚€ μ„€μ •
response.addCookie(accessTokenCookie);
response.addCookie(refreshTokenCookie);

πŸ’‘ Codebase verification

πŸ› οΈ Refactor suggestion

λ¦¬ν”„λ ˆμ‹œ 토큰 λ³΄μ•ˆ 취약점이 ν™•μΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ν˜„μž¬ κ΅¬ν˜„μ—μ„œ λ‹€μŒκ³Ό 같은 λ³΄μ•ˆ 취약점이 λ°œκ²¬λ˜μ—ˆμŠ΅λ‹ˆλ‹€:

  1. λ¦¬ν”„λ ˆμ‹œ 토큰 μž¬μ‚¬μš© 감지 λ©”μ»€λ‹ˆμ¦˜μ΄ μ—†μŠ΅λ‹ˆλ‹€.

    • νƒˆμ·¨λœ λ¦¬ν”„λ ˆμ‹œ ν† ν°μ˜ μ•…μš©μ„ λ°©μ§€ν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  2. 토큰 ꡐ체(Rotation) μ‹œ 이전 ν† ν°μ˜ λ¬΄νš¨ν™”κ°€ 이루어지지 μ•ŠμŠ΅λ‹ˆλ‹€.

    • Member μ—”ν‹°ν‹°μ˜ updateRefreshTokenλ§ŒμœΌλ‘œλŠ” μΆ©λΆ„ν•œ λ³΄μ•ˆμ„ μ œκ³΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  3. λ¦¬ν”„λ ˆμ‹œ 토큰 λΈ”λž™λ¦¬μŠ€νŠΈλ‚˜ 토큰 계보 좔적이 κ΅¬ν˜„λ˜μ–΄ μžˆμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

    • /refresh μ—”λ“œν¬μΈνŠΈμ—μ„œ μƒˆλ‘œμš΄ 토큰 λ°œκΈ‰ μ‹œ 이전 ν† ν°μ˜ 좔적이 λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

λ‹€μŒ λ³΄μ•ˆ κΈ°λŠ₯의 μΆ”κ°€λ₯Ό ꢌμž₯λ“œλ¦½λ‹ˆλ‹€:

  • λ¦¬ν”„λ ˆμ‹œ 토큰 μ‚¬μš© μ‹œ 이전 토큰 μ¦‰μ‹œ λ¬΄νš¨ν™”
  • 토큰 계보(family) 좔적을 ν†΅ν•œ μž¬μ‚¬μš© 감지
  • λ¦¬ν”„λ ˆμ‹œ 토큰 λΈ”λž™λ¦¬μŠ€νŠΈ 관리
πŸ”— Analysis chain

λ¦¬ν”„λ ˆμ‹œ 토큰 λ³΄μ•ˆ κ°•ν™”κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

λ¦¬ν”„λ ˆμ‹œ ν† ν°μ˜ λ³΄μ•ˆμ„ κ°•ν™”ν•˜κΈ° μœ„ν•΄ λ‹€μŒ κΈ°λŠ₯λ“€μ˜ κ΅¬ν˜„μ„ κ³ λ €ν•΄μ£Όμ„Έμš”:

  1. λ¦¬ν”„λ ˆμ‹œ 토큰 ꡐ체 (Rotation)
  2. λ¦¬ν”„λ ˆμ‹œ 토큰 μž¬μ‚¬μš© 감지
  3. λ¦¬ν”„λ ˆμ‹œ 토큰 만료 μ‹œκ°„ 검증

λ¦¬ν”„λ ˆμ‹œ 토큰 κ΅¬ν˜„μ΄ μ μ ˆν•œμ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ λ‹€μŒ 검증을 μ‹€ν–‰ν•©λ‹ˆλ‹€:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify refresh token implementation completeness

# Check for token rotation implementation
echo "Checking for token rotation..."
rg -l 'refreshToken.*rotation|rotateRefreshToken|updateRefreshToken'

# Check for token reuse detection
echo "Checking for reuse detection..."
rg -l 'tokenReuse|detectReuse|validateRefreshToken'

# Check for proper token validation
echo "Checking for token validation..."
rg -l 'validateToken|verifyToken|checkToken'

Length of output: 628


Script:

#!/bin/bash
# Check the actual implementation details

echo "=== Member.java refresh token handling ==="
rg -A 10 -B 10 'refreshToken' src/main/java/com/mycom/socket/go_socket/entity/Member.java

echo -e "\n=== RefreshController implementation ==="
cat src/main/java/com/mycom/socket/auth/controller/RefreshController.java

echo -e "\n=== JWTUtil token validation ==="
rg -A 5 'validateToken|verifyToken|checkToken' src/main/java/com/mycom/socket/auth/jwt/JWTUtil.java

Length of output: 4082


Script:

#!/bin/bash
# Get complete JWTUtil implementation
echo "=== Complete JWTUtil implementation ==="
cat src/main/java/com/mycom/socket/auth/jwt/JWTUtil.java

Length of output: 4497


return LoginResponse.of(member.getEmail(), member.getNickname());
}
Expand Down Expand Up @@ -101,5 +112,6 @@ public RegisterResponse register(RegisterRequest request) {
*/
public void logout(HttpServletResponse response) {
response.addCookie(cookieUtil.createExpiredAuthCookie()); // CookieUtil μ‚¬μš©
response.addCookie(cookieUtil.createExpiredRefreshCookie());
}
}
12 changes: 5 additions & 7 deletions src/main/java/com/mycom/socket/auth/service/MailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,13 @@ public EmailVerificationResponse verifyCode(String email, String code) {
throw new BaseException("μœ νš¨ν•˜μ§€ μ•Šμ€ 인증 μ½”λ“œ ν˜•μ‹μž…λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST);
}

try {
String saveCode = redisService.getCode(code); // μΈμ¦μ½”λ“œ 검증
if(!saveCode.equals(code)) {
throw new BaseException("인증 μ½”λ“œκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST);
}
return EmailVerificationResponse.of("이메일 인증이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
} catch (Exception e) {
String savedCode = redisService.getCode(email); // Redisμ—μ„œ μ½”λ“œλ₯Ό κ°€μ Έμ˜΄

Comment on lines +93 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Redisμ—μ„œ κ°€μ Έμ˜¨ μ½”λ“œμ˜ null 체크가 ν•„μš”ν•©λ‹ˆλ‹€.

savedCodeκ°€ null인 경우 (μ½”λ“œκ°€ λ§Œλ£Œλ˜μ—ˆκ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 경우) NullPointerException이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 null 체크λ₯Ό μΆ”κ°€ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€:

 String savedCode = redisService.getCode(email);  // Redisμ—μ„œ μ½”λ“œλ₯Ό κ°€μ Έμ˜΄
+if (savedCode == null) {
+    throw new BaseException("λ§Œλ£Œλ˜μ—ˆκ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 인증 μ½”λ“œμž…λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST);
+}
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String savedCode = redisService.getCode(email); // Redisμ—μ„œ μ½”λ“œλ₯Ό κ°€μ Έμ˜΄
String savedCode = redisService.getCode(email); // Redisμ—μ„œ μ½”λ“œλ₯Ό κ°€μ Έμ˜΄
if (savedCode == null) {
throw new BaseException("λ§Œλ£Œλ˜μ—ˆκ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 인증 μ½”λ“œμž…λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST);
}

if(!savedCode.equals(code)){
throw new BaseException("인증 μ½”λ“œκ°€ μΌμΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.", HttpStatus.BAD_REQUEST);
}
redisService.saveVerifiedEmail(email);
return EmailVerificationResponse.of("이메일 인증이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void saveVerifiedEmail(String email) {
*/
public boolean isEmailVerified(String email) {
Object verified = redisTemplate.opsForValue().get(VERIFIED_EMAIL_PREFIX + email);
return "true".equals(verified);
return verified != null && "true".equals(verified.toString());
}

}
9 changes: 6 additions & 3 deletions src/test/java/com/mycom/socket/member/service/LoginTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mycom.socket.member.service;

import com.mycom.socket.auth.config.JWTProperties;
import com.mycom.socket.auth.dto.request.LoginRequest;
import com.mycom.socket.auth.dto.response.LoginResponse;
import com.mycom.socket.auth.jwt.JWTUtil;
Expand All @@ -13,7 +14,6 @@
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
Expand All @@ -36,6 +36,9 @@ class LoginTest {
@Mock
private PasswordEncoder passwordEncoder;

@Mock
private JWTProperties jwtProperties;

Comment on lines +39 to +41
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

λ¦¬ν”„λ ˆμ‹œ 토큰 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ 보완 ν•„μš”

λ‹€μŒ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ“€μ„ μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€:

  1. λ¦¬ν”„λ ˆμ‹œ 토큰 만료 μ‹œλ‚˜λ¦¬μ˜€
  2. λ¦¬ν”„λ ˆμ‹œ 토큰을 ν†΅ν•œ μ•‘μ„ΈμŠ€ 토큰 κ°±μ‹ 
  3. 잘λͺ»λœ λ¦¬ν”„λ ˆμ‹œ 토큰 처리
  4. 토큰 νƒ€μž… 검증
@Test
void λ¦¬ν”„λ ˆμ‹œν† ν°_만료() {
    // given
    String expiredToken = "expired.refresh.token";
    when(jwtUtil.validateToken(expiredToken, "REFRESH_TOKEN")).thenReturn(false);
    
    // when & then
    assertThrows(BadRequestException.class, 
        () -> authService.refreshAccessToken(expiredToken));
}

Also applies to: 76-76, 88-89

@Mock
private JWTUtil jwtUtil;

Expand Down Expand Up @@ -70,7 +73,7 @@ class LoginTest {

when(memberRepository.findByEmail(email)).thenReturn(Optional.of(member));
when(passwordEncoder.matches(password, encodedPassword)).thenReturn(true);
when(jwtUtil.createToken(email)).thenReturn(token);
when(jwtUtil.createToken(email, jwtProperties.getRefreshTokenValidityInSeconds())).thenReturn(token);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

λ¦¬ν”„λ ˆμ‹œ 토큰 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ 보완이 ν•„μš”ν•©λ‹ˆλ‹€.

ν˜„μž¬ ν…ŒμŠ€νŠΈλŠ” λ¦¬ν”„λ ˆμ‹œ 토큰 μƒμ„±λ§Œ κ²€μ¦ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λ‹€μŒ μ‹œλ‚˜λ¦¬μ˜€μ— λŒ€ν•œ ν…ŒμŠ€νŠΈλ₯Ό μΆ”κ°€ν•΄μ£Όμ„Έμš”:

  • λ¦¬ν”„λ ˆμ‹œ 토큰 만료 μ‹œλ‚˜λ¦¬μ˜€
  • λ¦¬ν”„λ ˆμ‹œ 토큰을 ν†΅ν•œ μ•‘μ„ΈμŠ€ 토큰 κ°±μ‹ 
  • 잘λͺ»λœ λ¦¬ν”„λ ˆμ‹œ 토큰 처리

ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ κ΅¬ν˜„μ— 도움이 ν•„μš”ν•˜μ‹œλ‹€λ©΄ 말씀해 μ£Όμ„Έμš”.

Also applies to: 88-89

when(cookieUtil.createAuthCookie(token)).thenReturn(authCookie); // CookieUtil λ™μž‘ μ •μ˜

// when
Expand All @@ -82,7 +85,7 @@ class LoginTest {
assertEquals(nickname, response.nickname());
verify(memberRepository).findByEmail(email);
verify(passwordEncoder).matches(password, encodedPassword);
verify(jwtUtil).createToken(email);
verify(jwtUtil).createToken(email, jwtProperties.getRefreshTokenValidityInSeconds());
verify(cookieUtil).createAuthCookie(token);
}

Expand Down