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

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);

return TokenResponse.of(newAccessToken);
}

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

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

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

}

/**
* λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 생성
*/
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

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


/**
* λ¦¬ν”„λ ˆμ‹œ 토큰 μΏ ν‚€ 만료 처리
*/
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

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);

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

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

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

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
Loading