Skip to content

Conversation

@efdao
Copy link
Collaborator

@efdao efdao commented Mar 19, 2025

#️⃣ 연관된 이슈>

📝 작업 내용> 이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

  1. 로그인시 setCookie로 refreshToken 쿠키를 추가합니다.
  2. 엑세스토큰 인증 실패시 클라이언트에서 /api/v1/refresh-token 엔드포인트로 refresh 요청을 보냅니다.
  3. refreshToken 검증 성공 시 새로운 access token과 refresh token을 발행합니다.
  4. 검증 실패시 O006 INVALID_REFRESH_TOKEN(401) 반환합니다.
  • JwtProvider에 리프레시 토큰 관련 로직 추가했습니다.
  • CookieUtil 생성하여 리프레시 토큰 쿠키 생성, 조회 관리합니다.
  • Authservice에 refreshToken 관련 로직 추가했습니다.
  • AuthController에 /api/v1/refresh-token 엔드포인트 추가했습니다.

🙏 여기는 꼭 봐주세요! > 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

  • 현재 refresh token 검증 실패 시 401을 보내고 있는데 이 부분에 대해서 의견 주시면 감사하겠습니다.
  • local로 테스트해봤을 때 쿠키 심어지는것은 확인했고, refresh 요청 시 postman으로 cookie 주입하였을 때는 정상 동작 확인했습니다.
    • 브라우저에서 요청시 자동으로 쿠키가 담기지 않아 그 과정은 테스트 못해봤습니다. 확인해주시면 감사합니다.

Summary by CodeRabbit

  • New Features
    • Enhanced the login process by adding support for refresh tokens, improving session management.
    • Introduced a new endpoint for refreshing tokens, allowing users to maintain access without re-authentication.
  • Tests
    • Expanded testing to validate the improved authentication flow and token refresh functionality.

@efdao efdao requested a review from kwon204 as a code owner March 19, 2025 07:20
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2025

Walkthrough

This pull request updates the authentication flow by modifying login functionality to include refresh token cookie management and introducing a refresh token endpoint. The changes propagate through the controller, service, JWT provider, and testing components, and include new configurations and utility classes for managing cookies and refresh token properties. Additionally, error handling for refresh tokens has been enhanced with a new error code, and the JWT filter has been adjusted for the refresh endpoint.

Changes

Files Summary
backend/src/.../AuthController.java
backend/src/.../AuthService.java
Modified login endpoint to include a HttpServletResponse for setting refresh token cookies; added a refreshToken method and updated login method signature to return a TokenDto.
backend/src/.../TokenDto.java Introduced TokenDto to encapsulate OAuth response and refresh token cookie.
backend/src/.../JwtProvider.java Added methods to create a refresh token and extract the user ID from a token.
backend/src/.../CookieConfig.java
backend/src/.../RefreshTokenCookieProperties.java
backend/src/.../CookieUtil.java
backend/src/test/resources/application.yml
Added new configuration classes and a utility for managing refresh token cookies, including properties binding from the configuration.
backend/src/.../ErrorCode.java
backend/src/.../JwtAuthFilter.java
Enhanced error handling by adding the INVALID_REFRESH_TOKEN error code and updated the JWT filter to exclude the refresh token endpoint.
backend/src/test/.../AuthServiceTest.java Extended tests to verify refresh token functionality and updated login tests to align with the new response structure.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant AC as AuthController
    participant AS as AuthService
    participant JP as JwtProvider
    C ->> AC: POST /login (credentials)
    AC ->> AS: login(code, response)
    AS ->> JP: createAccessToken & createRefreshToken
    JP -->> AS: TokenDto {oAuthResponse, refreshTokenCookie}
    AS -->> AC: TokenDto
    AC ->> C: Response with OAuthResponse and refresh token cookie
Loading
sequenceDiagram
    participant C as Client
    participant AC as AuthController
    participant AS as AuthService
    participant CU as CookieUtil
    participant JP as JwtProvider
    C ->> AC: POST /api/v1/refresh-token (with cookie)
    AC ->> AS: refreshToken(request, response)
    AS ->> CU: getRefreshToken(cookies)
    CU -->> AS: refresh token value
    AS ->> JP: validate token & create new access token
    JP -->> AS: TokenDto {new oAuthResponse, new refreshTokenCookie}
    AS -->> AC: TokenDto
    AC ->> C: Response with new OAuthResponse and refreshed cookie
Loading

Possibly related PRs

Suggested reviewers

  • kwon204
  • dioo1461

Poem

I'm a rabbit hopping through the code maze bright,
Tokens refreshed in cookies, a marvelous sight!
Login flows and refresh calls weave a tale so neat,
Bugs nibbled and fixed with every little beat.
In the world of endpoints, I joyfully trot,
Celebrating clean code with a carrot-filled plot!
🥕 Happy coding from this little bunny!

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c06ab6 and b0927b7.

📒 Files selected for processing (1)
  • backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (4 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (1)
backend/src/main/java/endolphin/backend/global/util/TimeUtil.java (1) (1)
  • TimeUtil (10-104)
🔇 Additional comments (3)
backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (3)

3-3: Dependencies and imports are properly added for refresh token support.

The necessary imports and dependency for the CookieUtil have been correctly added to support the refresh token functionality.

Also applies to: 13-13, 15-15, 31-31


44-56: Login method properly implements refresh token generation.

The login method has been updated to return TokenDto instead of OAuthResponse and now generates a refresh token alongside the access token. The implementation correctly uses the injected CookieUtil to create a refresh token cookie.


66-74: Refresh method correctly implements token rotation.

The refresh method correctly:

  1. Retrieves the user ID from the refresh token
  2. Fetches the user from the database
  3. Creates a new access token
  4. Generates a new refresh token (implementing token rotation)
  5. Returns both tokens in the appropriate format

This follows security best practices by rotating refresh tokens on each use.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@efdao efdao requested a review from dioo1461 March 19, 2025 07:20
@efdao efdao added the 🛠️ BE Backend label Mar 19, 2025
@efdao efdao added this to the 7️⃣ 7차 스프린트 milestone Mar 19, 2025
@efdao efdao linked an issue Mar 19, 2025 that may be closed by this pull request
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
backend/src/main/java/endolphin/backend/domain/auth/dto/TokenDto.java (1)

5-10: Consider adding JavaDoc documentation to the TokenDto class

The record structure looks good for encapsulating both the access token (via OAuthResponse) and refresh token cookie. Adding JavaDoc comments would improve the maintainability by documenting the purpose of this DTO and explaining each field, especially for other developers who might use this class in the future.

 package endolphin.backend.domain.auth.dto;

 import jakarta.servlet.http.Cookie;

+/**
+ * Data transfer object that encapsulates authentication tokens.
+ * Contains the OAuth response (with access token) and the refresh token cookie.
+ */
 public record TokenDto(
+    /**
+     * Contains the OAuth response including the access token
+     */
     OAuthResponse oAuthResponse,
+    /**
+     * The HTTP cookie containing the refresh token
+     */
     Cookie refreshTokenCookie
 ) {

 }
backend/src/main/java/endolphin/backend/global/util/CookieUtil.java (1)

24-36: Consider enhancing cookie retrieval with more robust error handling.

The method properly handles null cookies array, but consider enhancing error handling when the refresh token cookie is not found. Some options:

  1. Log a warning when the cookie is missing
  2. Return an Optional instead of null
  3. Throw a specific exception type for better error traceability
-    public String getRefreshToken(Cookie[] cookies) {
+    public Optional<String> getRefreshToken(Cookie[] cookies) {
         if (cookies == null) {
-            return null;
+            return Optional.empty();
         }

         for (Cookie cookie : cookies) {
             if (cookie.getName().equals(properties.name())) {
-                return cookie.getValue();
+                return Optional.ofNullable(cookie.getValue());
             }
         }

-        return null;
+        return Optional.empty();
     }
backend/src/main/java/endolphin/backend/global/security/JwtProvider.java (2)

42-54: Well-implemented refresh token creation method.

The createRefreshToken method is implemented correctly with proper validation attributes:

  • Sets only essential claims (userId) for security
  • Uses the configured refresh token validity period
  • Follows the same pattern as the access token creation

I'd recommend adding a brief Javadoc comment to document the method's purpose and parameters.


55-64: Improve error handling in token validation.

The current implementation throws a generic RuntimeException when token validation fails. Consider:

  1. Using a custom exception type for JWT validation errors
  2. Including the original exception cause for better debugging
  3. Providing more specific error messages based on the exception type
public Jws<Claims> validateToken(String token) {
    try {
        return Jwts.parser()
            .verifyWith(key)
            .build()
            .parseSignedClaims(token);
    } catch (JwtException | IllegalArgumentException e) {
-        throw new RuntimeException("Invalid or expired JWT token");
+        throw new JwtAuthenticationException("Invalid or expired JWT token", e);
    }
}

This would require creating a custom JwtAuthenticationException class that extends an appropriate exception type, providing better error handling throughout the application.

backend/src/test/java/endolphin/backend/domain/auth/AuthServiceTest.java (1)

95-127: Refresh token test scenario is good, but cookie naming mismatch is confusing.
You're creating a cookie named "test" rather than "refreshToken", yet you mock cookieUtil to return "test-refresh-token". This doesn’t replicate real usage, where cookieUtil.getRefreshToken() checks for a cookie named "refreshToken". Also, consider adding negative tests for missing/invalid cookies to increase coverage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 741618c and 8c06ab6.

📒 Files selected for processing (11)
  • backend/src/main/java/endolphin/backend/domain/auth/AuthController.java (2 hunks)
  • backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (4 hunks)
  • backend/src/main/java/endolphin/backend/domain/auth/dto/TokenDto.java (1 hunks)
  • backend/src/main/java/endolphin/backend/global/config/CookieConfig.java (1 hunks)
  • backend/src/main/java/endolphin/backend/global/config/RefreshTokenCookieProperties.java (1 hunks)
  • backend/src/main/java/endolphin/backend/global/error/exception/ErrorCode.java (1 hunks)
  • backend/src/main/java/endolphin/backend/global/security/JwtAuthFilter.java (1 hunks)
  • backend/src/main/java/endolphin/backend/global/security/JwtProvider.java (3 hunks)
  • backend/src/main/java/endolphin/backend/global/util/CookieUtil.java (1 hunks)
  • backend/src/test/java/endolphin/backend/domain/auth/AuthServiceTest.java (4 hunks)
  • backend/src/test/resources/application.yml (1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (1)
backend/src/main/java/endolphin/backend/global/util/TimeUtil.java (1) (1)
  • TimeUtil (10-104)
backend/src/main/java/endolphin/backend/global/security/JwtProvider.java (1)
backend/src/main/java/endolphin/backend/global/util/TimeUtil.java (1) (1)
  • TimeUtil (10-104)
🔇 Additional comments (13)
backend/src/main/java/endolphin/backend/global/error/exception/ErrorCode.java (1)

53-53: Addition of INVALID_REFRESH_TOKEN error code looks good

The new error code is correctly placed in the OAuth section, uses an appropriate HTTP status code (401 UNAUTHORIZED), and follows the existing code pattern with the "O" prefix followed by a sequential number. This aligns with the PR objective to handle refresh token validation failures properly.

backend/src/main/java/endolphin/backend/global/security/JwtAuthFilter.java (1)

66-66: Correct exclusion of refresh token endpoint from JWT authentication

Adding the refresh token endpoint to the excluded paths is necessary for the authentication flow to work properly. When a user's access token expires, they need to be able to request a new one using their refresh token without requiring a valid JWT.

backend/src/main/java/endolphin/backend/global/config/CookieConfig.java (1)

10-13:

Details

✅ Verification successful

Ensure Secure flag is set when using SameSite=None

Setting SameSite=None means cookies will be sent in all cross-site requests, which could make them vulnerable to CSRF attacks. When using SameSite=None, the Secure flag should also be set to ensure cookies are only sent over HTTPS. This is especially important for refresh token cookies given their sensitive nature.

Please verify that the CookieUtil class (mentioned in the PR objectives) sets the Secure flag on the refresh token cookies:


🏁 Script executed:

#!/bin/bash
# Search for the CookieUtil class and check if it sets the Secure flag on cookies
fd -t f CookieUtil.java -x cat {} | grep -A 15 "createRefreshTokenCookie"

Length of output: 662


Verified: Secure Flag is Configured for SameSite=None Cookies

The CookieUtil class correctly sets the Secure flag on the refresh token cookies by calling:

refreshTokenCookie.setSecure(properties.secure());
  • This confirms that when using SameSite=None (configured in CookieConfig.java), the Secure flag is handled via the properties.secure() value.
  • Please ensure that the secure property is appropriately set to true in production to enforce HTTPS-only transmission.
backend/src/main/java/endolphin/backend/global/config/RefreshTokenCookieProperties.java (1)

5-13: Well-structured configuration properties for refresh token cookie.

The use of Java records for configuration properties is a good choice as it provides immutability and concise syntax. This class properly encapsulates all necessary cookie properties for the refresh token implementation.

backend/src/test/resources/application.yml (1)

57-64: Verify refresh token configuration values for production use.

The refresh token configuration has some values that might be suitable for testing but could be problematic in production:

  1. expired: 1000 sets the token validity to just 1 second
  2. max-age: 0 means the cookie will expire immediately

These values should be increased significantly for production environments to allow proper refresh token functionality.

backend/src/main/java/endolphin/backend/global/util/CookieUtil.java (1)

8-22: Clean implementation of cookie management utility.

The CookieUtil class properly leverages the configuration properties and provides a clean method for creating refresh token cookies. The implementation correctly sets all required cookie attributes.

backend/src/main/java/endolphin/backend/global/security/JwtProvider.java (2)

19-20: LGTM: Properly injecting refresh token expiration from configuration.

The addition of the refreshValidityInMs property with proper annotation to inject from configuration is well-implemented.


66-68: LGTM: Useful utility method for extracting user ID.

This method provides a convenient way to extract the user ID from a validated token. It properly reuses the validateToken method to ensure the token is valid before attempting to extract the payload.

backend/src/main/java/endolphin/backend/domain/auth/AuthService.java (2)

3-3: New imports and dependencies look consistent.
The addition of TokenDto, CookieUtil, and HttpServletRequest dependencies, along with a final CookieUtil cookieUtil field, appears correctly injected via the constructor. No immediate issues noted here.

Also applies to: 13-13, 15-15, 31-31


44-55: Refactored login method returning TokenDto is well-structured.
Encapsulating both the access token and the refresh token cookie in TokenDto makes sense. Ensure that any calling code (controllers/tests) is also updated accordingly so that the refresh token is handled exclusively via secure cookies.

backend/src/main/java/endolphin/backend/domain/auth/AuthController.java (1)

5-5: New imports for TokenDto and HttpServletRequest look good.
No issues spotted with these additional imports.

Also applies to: 8-8

backend/src/test/java/endolphin/backend/domain/auth/AuthServiceTest.java (2)

7-7: Additional imports and mocks align with new token-based logic.
The use of when(...) and TokenDto references appears valid, preparing for the new refresh token test scenarios.

Also applies to: 10-10, 19-19, 20-20, 21-21, 29-29


48-49: Mocking the CookieUtil dependency is appropriate.
Injecting CookieUtil as a mock lets you accurately simulate cookie behaviors during tests. No issues noted.

Comment on lines +58 to +64
public TokenDto refresh(HttpServletRequest request) {
String refreshToken = cookieUtil.getRefreshToken(request.getCookies());
if(refreshToken == null || refreshToken.isBlank()) {
throw new OAuthException(ErrorCode.INVALID_REFRESH_TOKEN);
}

//TODO: refresh 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

Incomplete refresh token validation.
You've added a clear check for a null or blank refresh token, which is good. However, the //TODO: refresh token 검증 comment indicates missing signature or expiration checks. This could be a security risk.

Would you like assistance generating code to validate token integrity and expiration?

Comment on lines +50 to +58
public ResponseEntity<OAuthResponse> login(@Valid @RequestBody LoginRequest request,
HttpServletResponse response) {
TokenDto tokens = authService.login(request.code());
OAuthResponse oAuthResponse = tokens.oAuthResponse();

response.addCookie(tokens.refreshTokenCookie());

return ResponseEntity.ok(oAuthResponse);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Login endpoint sets refresh token in cookie successfully, but ensure security flags.
When adding the refresh token cookie, consider marking it as HttpOnly, Secure, and SameSite to mitigate XSS and CSRF vectors. By default, these flags are not set, potentially allowing malicious scripts to access the cookie or enabling cross-site usage.

Comment on lines +60 to +69
@Operation(summary = "액세스 토큰 재발급", description = "리프레시 토큰을 사용하여 새로운 액세스 토큰을 발급받습니다.")
@PostMapping("/api/v1/refresh-token")
public ResponseEntity<OAuthResponse> refreshToken(HttpServletRequest request,
HttpServletResponse response) {

TokenDto newTokens = authService.refresh(request);

response.addCookie(newTokens.refreshTokenCookie());

return ResponseEntity.ok(newTokens.oAuthResponse());
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

New refresh endpoint is consistent, but apply the same cookie security measures.
Again, ensure the refresh token cookie is configured with best-practice flags (HttpOnly, Secure, SameSite) here as well. This helps protect the token from unauthorized scripts and cross-site attacks.

Copy link
Contributor

@kwon204 kwon204 left a comment

Choose a reason for hiding this comment

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

refresh token 검증 시 에러는 401로 하면 될 거 같다는 생각이 듭니다

LocalDateTime expiredAt = TimeUtil.getNow().plus(expired, ChronoUnit.MILLIS);

return new TokenDto(new OAuthResponse(newAccessToken, expiredAt),
cookieUtil.createRefreshTokenCookie(refreshToken));
Copy link
Contributor

Choose a reason for hiding this comment

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

just ask; refresh token을 새로 발급하지는 않는건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

누락됐네요 추가하겠습니다

Copy link
Contributor

Choose a reason for hiding this comment

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

제 생각에도 401에러 반환하면 될 것 같습니다~

@dioo1461
Copy link
Contributor

고생하셨습니다~ api 연동해보고, 필요하면 커멘트로 질문 남기겠습니다

Copy link
Contributor

@kwon204 kwon204 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다.

@dioo1461
Copy link
Contributor

dioo1461 commented Mar 19, 2025

브라우저 새로고침 시 쿠키에서 리프레시 토큰이 사라집니다..
디스코드에 올라온 application-dev.yml 설정은 적용된 상태입니다.
혹시 제가 모르고 있는 부분이 있다면 알려주시면 감사하겠습니다.

SHANA.2025-03-19.22-09-46.mp4

(+25-03-20 백엔드 지인한테 물어보니 브라우저 개발자 도구에서 쿠키가 안 보여도 실제로는 헤더에 쿠키가 제대로 들어갈 거라고 합니다.

https://okky.kr/questions/1151028
그리고 본래 http only secure하면 클라이언트 단에서 확인이 불가능합니다. Api 호출 해보시면 네트워크 탭에서 해더에 해당 토큰이 붙어 있을거에요.
클라이언트 단에서 js로 조작이 불가능해서 http only secure 쿠키를 사용하는거에요

위 글에 의하면 맞는거같네요..? 정상작동한다는 가정 하에 작업해보겠습니다)

+ 프론트엔드 코드 작성해 테스트해 보았으나 매번 401 에러 반환됩니다.
아무래도 쿠키가 날아가는 게 맞는 것 같아요

문제 찾았습니다! sameSite=none 일 때, secure=true 여야 쿠키가 제대로 들어간다고 합니다.

application-dev.yml에서 쿠키에 secure=true 주입하고, 로컬에서 https 설정 후 테스트해보니 제대로 작동합니다.


추가로, 최초 로그인 시 backend_api 컨테이너에 아래 에러가 발생합니다.
dev 브랜치에서 테스트해보아도 동일한 에러가 발생합니다. 원래 있던 문제인 듯..? 근데 에러 떠도 로그인은 되긴 합니다.

ERROR 1 [task-3] o.h.e.jdbc.spi.SqlExceptionHelper  - Duplicate entry 'dioo14610143@gmail.com' for key 'calendar.UKq1dmo2dr4qng4soemih8ai6ax'
ERROR 1 [task-3] o.s.a.i.SimpleAsyncUncaughtExceptionHandler  - Unexpected exception occurred invoking async method: public void endolphin.backend.global.google.event.handler.GoogleEventHandler.login(endolphin.backend.domain.user.event.LoginEvent)
org.springframework.dao.DataIntegrityViolationException: could not execute statement [Duplicate entry 'dioo14610143@gmail.com' for key 'calendar.UKq1dmo2dr4qng4soemih8ai6ax'] [insert into calendar (calendar_id,channel_expiration,channel_id,created_at,description,name,resource_id,sync_token,updated_at,user_id) values (?,?,?,?,?,?,?,?,?,?)]; SQL [insert into calendar (calendar_id,channel_expiration,channel_id,created_at,description,name,resource_id,sync_token,updated_at,user_id) values (?,?,?,?,?,?,?,?,?,?)]; constraint [calendar.UKq1dmo2dr4qng4soemih8ai6ax]
 at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290)
...

@kwon204
Copy link
Contributor

kwon204 commented Mar 20, 2025

브라우저 새로고침 시 쿠키에서 리프레시 토큰이 사라집니다.. 디스코드에 올라온 application-dev.yml 설정은 적용된 상태입니다. 혹시 제가 모르고 있는 부분이 있다면 알려주시면 감사하겠습니다.

SHANA.2025-03-19.22-09-46.mp4
(+25-03-20 백엔드 지인한테 물어보니 브라우저 개발자 도구에서 쿠키가 안 보여도 실제로는 헤더에 쿠키가 제대로 들어갈 거라고 합니다.

https://okky.kr/questions/1151028
그리고 본래 http only secure하면 클라이언트 단에서 확인이 불가능합니다. Api 호출 해보시면 네트워크 탭에서 해더에 해당 토큰이 붙어 있을거에요.
클라이언트 단에서 js로 조작이 불가능해서 http only secure 쿠키를 사용하는거에요

위 글에 의하면 맞는거같네요..? 정상작동한다는 가정 하에 작업해보겠습니다)

  • 프론트엔드 코드 작성해 테스트해 보았으나 매번 401 에러 반환됩니다. 아무래도 쿠키가 날아가는 게 맞는 것 같아요

추가로, 최초 로그인 시 backend_api 컨테이너에 아래 에러가 발생합니다. dev 브랜치에서 테스트해보아도 동일한 에러가 발생합니다. 원래 있던 문제인 듯..? 근데 에러 떠도 로그인은 되긴 합니다.

ERROR 1 [task-3] o.h.e.jdbc.spi.SqlExceptionHelper  - Duplicate entry 'dioo14610143@gmail.com' for key 'calendar.UKq1dmo2dr4qng4soemih8ai6ax'
ERROR 1 [task-3] o.s.a.i.SimpleAsyncUncaughtExceptionHandler  - Unexpected exception occurred invoking async method: public void endolphin.backend.global.google.event.handler.GoogleEventHandler.login(endolphin.backend.domain.user.event.LoginEvent)
org.springframework.dao.DataIntegrityViolationException: could not execute statement [Duplicate entry 'dioo14610143@gmail.com' for key 'calendar.UKq1dmo2dr4qng4soemih8ai6ax'] [insert into calendar (calendar_id,channel_expiration,channel_id,created_at,description,name,resource_id,sync_token,updated_at,user_id) values (?,?,?,?,?,?,?,?,?,?)]; SQL [insert into calendar (calendar_id,channel_expiration,channel_id,created_at,description,name,resource_id,sync_token,updated_at,user_id) values (?,?,?,?,?,?,?,?,?,?)]; constraint [calendar.UKq1dmo2dr4qng4soemih8ai6ax]
 at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:290)
...

스크린샷 2025-03-20 125423

컨테니어에서 발생하는 에러는 영상 확인해보니 로그인 요청을 두 번 보내고 있어서 Duplicate entry 'dioo14610143@gmail.com' for key 에러가 발생하는 거 같습니다.

@dioo1461
Copy link
Contributor

컨테니어에서 발생하는 에러는 영상 확인해보니 로그인 요청을 두 번 보내고 있어서 Duplicate entry 'dioo14610143@gmail.com' for key 에러가 발생하는 거 같습니다.

아 그러면 development 환경에서 React의 Strict Mode 때문에 컴포넌트가 두 번 렌더링되어 생기는 문제인 것 같네요, production 환경에서는 발생하지 않을 문제인 것 같습니다. 확인해주셔서 감사합니다~

@efdao
Copy link
Collaborator Author

efdao commented Mar 20, 2025

secure는 로컬에서는 false로 해두었습니다. 쿠키가 날아가는 건 여러가지 시도해보았는데 정확히 원인을 모르겠네요..
도메인 문제일 가능성이 가장 높을 것 같습니다.

@dioo1461
Copy link
Contributor

secure는 로컬에서는 false로 해두었습니다. 쿠키가 날아가는 건 여러가지 시도해보았는데 정확히 원인을 모르겠네요.. 도메인 문제일 가능성이 가장 높을 것 같습니다.

sameSite=none 일 때, secure=true 여야 쿠키가 제대로 들어간다고 합니다.

application-dev.yml에서 쿠키에 secure=true 주입하고, 로컬에서 https 설정 후 테스트해보니 제대로 작동합니다.

@efdao
Copy link
Collaborator Author

efdao commented Mar 20, 2025

아하 그런 문제였군요 다행이네요 확인해주셔서 감사합니다. 액세스토큰검증과 리프레시 토큰 검증이 똑같이 401인데 그부분은 괜찮으신가요?

@dioo1461
Copy link
Contributor

아하 그런 문제였군요 다행이네요 확인해주셔서 감사합니다. 액세스토큰검증과 리프레시 토큰 검증이 똑같이 401인데 그부분은 괜찮으신가요?

에러 코드로 식별 가능하니 괜찮을 것 같습니다!

@efdao efdao merged commit dce1972 into dev Mar 20, 2025
3 checks passed
@efdao efdao deleted the feature/be/refresh-token branch March 20, 2025 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🛠️ BE Backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 리프레시 토큰 구현

4 participants