Skip to content

Conversation

@dungbik
Copy link
Contributor

@dungbik dungbik commented Jul 28, 2025

📝 변경 내용


✅ 체크리스트

  • 코드가 정상적으로 동작함
  • 테스트 코드 통과함
  • 문서(README 등)를 최신화함
  • 코드 스타일 가이드 준수

💬 기타 참고 사항

Summary by CodeRabbit

  • 신규 기능

    • 소셜 로그인 성공 및 실패 시 별도의 리다이렉트 경로가 추가되었습니다.
    • 소셜 로그인 시 액세스 토큰이 쿼리 파라미터로 전달되며, 리프레시 토큰이 쿠키로 저장됩니다.
    • 소셜 로그인 기능이 새롭게 도입되어 JWT 토큰 쌍을 발급받을 수 있습니다.
  • 버그 수정

    • 지원하지 않는 소셜 제공자 및 미등록 소셜 계정에 대한 오류 메시지가 개선되었습니다.
    • 소셜 계정 이미 연동된 경우 HTTP 상태 코드가 충돌(CONFLICT)로 조정되었습니다.
  • 기타

    • 소셜 계정 연동과 로그인 처리 로직이 분리되어 안정성과 오류 처리, 로그가 향상되었습니다.
    • OAuth 상태 토큰 관리가 개선되어 사용자 인증 정보와 연동됩니다.
    • 보안 설정이 업데이트되어 소셜 인증 관련 엔드포인트 접근이 허용됩니다.
    • OAuth 인증 URI 생성 시 상태 파라미터가 null일 경우 제외되도록 개선되었습니다.
    • 소셜 계정 조회 시 사용자 정보가 함께 조회되도록 저장소 기능이 확장되었습니다.

@dungbik dungbik requested a review from stoneTiger0912 July 28, 2025 05:59
@dungbik dungbik self-assigned this Jul 28, 2025
@dungbik dungbik added the enhancement New feature or request label Jul 28, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Caution

Review failed

The pull request is closed.

## Walkthrough

OAuth 콜백 엔드포인트에서 소셜 계정 연동과 소셜 로그인 처리를 분리하고, 예외 처리 및 로깅을 개선하였습니다. 소셜 로그인 성공 시 리프레시 토큰 쿠키를 설정하며, 관련 엔드포인트와 에러코드, 리포지토리, 클라이언트 경로, 시큐리티 설정이 확장되었습니다.

## Changes

| Cohort / File(s) | Change Summary |
|------------------|---------------|
| **OAuthController 리팩토링 및 콜백 분리**<br> `src/main/java/project/flipnote/auth/controller/OAuthController.java` | 콜백 메서드를 소셜 계정 연동과 소셜 로그인으로 분리, 예외별 리다이렉트 및 로깅, 리프레시 토큰 쿠키 설정, 헬퍼 메서드 다수 추가, 서비스 호출 방식 변경 등 대규모 리팩토링 |
| **OAuthService 기능 확장**<br> `src/main/java/project/flipnote/auth/service/OAuthService.java` | 소셜 로그인 토큰 발급 로직 추가, provider 검증 강화, state 토큰 관리 개선, OAuth 유저 정보 요청 로직 분리 등 |
| **에러코드 추가 및 수정**<br> `src/main/java/project/flipnote/auth/exception/AuthErrorCode.java` | ALREADY_LINKED_SOCIAL_ACCOUNT 상태 변경(CONFLICT), 미등록 소셜 계정 및 지원하지 않는 소셜 제공자 에러코드 추가 |
| **클라이언트 경로 확장**<br> `src/main/java/project/flipnote/common/config/ClientProperties.java`,<br> `src/main/resources/application.yml` | SOCIAL_LOGIN_SUCCESS, SOCIAL_LOGIN_FAILURE 경로 및 설정 추가 |
| **OAuthApiClient 개선**<br> `src/main/java/project/flipnote/infra/oauth/OAuthApiClient.java` | state 파라미터가 null일 경우 쿼리에서 제외하도록 URI 빌더 로직 수정 |
| **UserOAuthLinkRepository 확장**<br> `src/main/java/project/flipnote/user/repository/UserOAuthLinkRepository.java` | provider, providerId로 user까지 fetch join하는 쿼리 메서드 추가 |
| **시큐리티 설정 보완**<br> `src/main/java/project/flipnote/common/security/config/SecurityConfig.java` | `/oauth2/authorization/{provider}` GET 요청도 인증 없이 허용하도록 변경 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant OAuthController
    participant OAuthService
    participant OAuthApiClient
    participant UserOAuthLinkRepository
    participant JwtComponent

    Client->>OAuthController: GET /oauth2/callback/{provider}?code=...&state=...
    OAuthController->>OAuthController: state 파라미터 체크
    alt state 존재 (소셜 연동)
        OAuthController->>OAuthService: linkSocialAccount(...)
        OAuthService->>OAuthApiClient: getAccessToken + getUserInfo
        OAuthService->>UserOAuthLinkRepository: 계정 연동 처리
        OAuthController-->>Client: 연동 성공/실패 리다이렉트
    else state 없음 (소셜 로그인)
        OAuthController->>OAuthService: socialLogin(...)
        OAuthService->>OAuthApiClient: getAccessToken + getUserInfo
        OAuthService->>UserOAuthLinkRepository: 연동된 계정 조회
        OAuthService->>JwtComponent: 토큰 페어 발급
        OAuthController-->>Client: 로그인 성공 리다이렉트 (액세스 토큰 쿼리, 리프레시 쿠키)
    end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • stoneTiger0912

Poem

🐰
새로워진 로그인, 연동도 척척
콜백 분리로 깔끔해진 껍데기
에러코드도 추가되어 든든해
리프레시 쿠키까지 완벽하게
토끼는 오늘도 코드를 춤추네!
🥕✨


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->

---

<details>
<summary>📜 Recent review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 71b5a0300aaca6dc7184d760a9a6e2475b86dcdc and 31a39d6e9c889e7a62776cfcd9913f428ca7944d.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/main/java/project/flipnote/auth/exception/AuthErrorCode.java` (1 hunks)
* `src/main/java/project/flipnote/user/repository/UserOAuthLinkRepository.java` (2 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings
<details>
<summary>🧪 Generate unit tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `feat/social-login`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=FlipNoteTeam/FlipNote-BE&utm_content=19):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `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 explain this code block.`
  -	`@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 explain its main purpose.`
  - `@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.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai generate unit tests` to generate unit tests 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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

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: 3

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1a23f23 and 71b5a03.

📒 Files selected for processing (8)
  • src/main/java/project/flipnote/auth/controller/OAuthController.java (3 hunks)
  • src/main/java/project/flipnote/auth/exception/AuthErrorCode.java (1 hunks)
  • src/main/java/project/flipnote/auth/service/OAuthService.java (5 hunks)
  • src/main/java/project/flipnote/common/config/ClientProperties.java (1 hunks)
  • src/main/java/project/flipnote/common/security/config/SecurityConfig.java (1 hunks)
  • src/main/java/project/flipnote/infra/oauth/OAuthApiClient.java (1 hunks)
  • src/main/java/project/flipnote/user/repository/UserOAuthLinkRepository.java (2 hunks)
  • src/main/resources/application.yml (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/project/flipnote/auth/exception/AuthErrorCode.java (2)
src/main/java/project/flipnote/common/exception/ErrorCode.java (1)
  • ErrorCode (3-10)
src/main/java/project/flipnote/auth/constants/AuthRedisKey.java (1)
  • AuthRedisKey (7-21)
src/main/java/project/flipnote/user/repository/UserOAuthLinkRepository.java (2)
src/main/java/project/flipnote/user/entity/UserOAuthLink.java (2)
  • UserOAuthLink (20-52)
  • UserOAuthLink (46-51)
src/main/java/project/flipnote/user/entity/User.java (1)
  • User (20-105)
🔇 Additional comments (12)
src/main/resources/application.yml (1)

65-66: 소셜 로그인 경로 설정이 올바르게 추가되었습니다.

새로운 클라이언트 경로 설정이 기존 소셜 링크 경로와 일관성 있게 구성되어 있으며, OAuth 컨트롤러에서 사용할 리다이렉트 URL을 제공합니다.

src/main/java/project/flipnote/common/config/ClientProperties.java (1)

22-23: PathKey enum에 소셜 로그인 관련 상수가 적절히 추가되었습니다.

새로운 상수들이 기존 네이밍 컨벤션을 따르고 있으며, application.yml의 설정과 일치합니다.

src/main/java/project/flipnote/common/security/config/SecurityConfig.java (1)

69-69: OAuth 인증 엔드포인트에 대한 접근 권한이 올바르게 설정되었습니다.

/oauth2/authorization/{provider} 엔드포인트를 인증 없이 허용하도록 설정한 것은 OAuth 플로우에서 표준적인 구성입니다.

src/main/java/project/flipnote/infra/oauth/OAuthApiClient.java (1)

81-94: 인증 URI 생성 로직이 개선되었습니다.

State 파라미터를 조건부로 추가하는 방식으로 변경하여 null 값이 URI에 포함되는 것을 방지했습니다. 이는 소셜 로그인과 소셜 계정 연동의 서로 다른 플로우를 지원하는 좋은 개선사항입니다.

src/main/java/project/flipnote/user/repository/UserOAuthLinkRepository.java (1)

20-30: 효율적인 JOIN FETCH 쿼리가 잘 구현되었습니다.

findByProviderAndProviderIdWithUser 메서드는 OAuth 링크와 연관된 사용자 엔티티를 한 번의 쿼리로 조회하는 효율적한 구현입니다. 소셜 로그인 플로우에서 N+1 문제를 방지할 수 있습니다.

src/main/java/project/flipnote/auth/exception/AuthErrorCode.java (1)

24-25: 적절한 오류 코드 추가입니다.

소셜 로그인 기능에 필요한 오류 코드들이 적절한 HTTP 상태 코드와 명확한 메시지로 추가되었습니다.

src/main/java/project/flipnote/auth/service/OAuthService.java (4)

46-57: 메서드 시그니처 개선이 잘 되었습니다.

UserAuth 객체를 사용하여 인증된 사용자와 미인증 사용자 모두를 처리할 수 있도록 개선되었습니다.


93-101: 소셜 로그인 구현이 명확합니다.

OAuth 사용자 정보 조회, 연동 계정 확인, JWT 토큰 생성의 흐름이 잘 구성되었습니다.


111-117: Provider 검증 로직이 개선되었습니다.

Optional을 사용한 null 안전성과 명확한 로깅이 추가되어 디버깅이 용이해졌습니다.


119-126: State 생성 로직이 잘 분리되었습니다.

인증 여부에 따라 state 토큰 생성을 조건부로 처리하여 소셜 연동과 소셜 로그인 플로우를 명확히 구분합니다.

src/main/java/project/flipnote/auth/controller/OAuthController.java (2)

65-70: 콜백 처리 플로우가 명확하게 분리되었습니다.

state 파라미터 유무로 소셜 연동과 소셜 로그인을 구분하는 로직이 간결하고 이해하기 쉽습니다.


125-132: 리프레시 토큰 쿠키 생성이 적절합니다.

JWT 설정의 만료 시간을 사용하여 쿠키를 생성하는 로직이 잘 구현되었습니다.

ResponseCookie refreshTokenCookie = null;
try {
TokenPair tokenPair = oAuthService.socialLogin(provider, code, codeVerifier, request);
location = buildLoginSuccessRedirectUri(tokenPair.accessToken());
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

보안 우려: Access Token이 URL에 노출됩니다.

Access Token을 쿼리 파라미터로 전달하면 서버 로그, 브라우저 히스토리, Referer 헤더 등에 노출될 위험이 있습니다.

더 안전한 방법을 고려해보세요:

  • Fragment identifier (#) 사용
  • 임시 코드 발급 후 클라이언트에서 토큰 교환
  • Secure cookie 사용

🌐 Web query:

What are the best practices for passing JWT access tokens to client applications after OAuth login?

💡 Result:

The best practices for passing JWT access tokens to client applications after OAuth login focus on minimizing security risks while ensuring tokens are used as intended. Critical considerations involve secure transmission, proper storage, token content, and lifecycle management.

Key Best Practices:

  • Always send JWTs over HTTPS. Never transmit tokens over unsecured (HTTP) channels, as interception would allow attackers to use the token to access protected resources[2][4][7].

  • Deliver access tokens using the Authorization header. After login, provide the JWT to the client, which should then store it temporarily in memory or as securely as possible and send it back in the Authorization: Bearer <token> header with each API request[7][9].

  • Limit storage risk:

    • Web apps: Prefer storing JWTs in HttpOnly, Secure cookies to mitigate XSS exposure. Avoid localStorage and sessionStorage for access tokens, as XSS can expose these[1].
    • Mobile apps: Store tokens using secure platform-specific methods only if absolutely necessary and for very short duration—ideally, keep them in volatile memory when possible[6].
    • Single-page apps (SPA): Short-lived tokens in memory are safest, but refresh tokens and persistent storage require careful risk analysis and controls[1][6].
  • Use short-lived access tokens. Set a brief expiration time (commonly 5–15 minutes). Use refresh tokens—secured as tightly as possible—to obtain new access tokens without re-authenticating the user[1][2][3][6].

  • Minimize token content. Only include claims strictly necessary for identity and authentication (e.g., user_id, org_id). Do not include permissions, sensitive data, or information that changes frequently—authorization logic should be handled by backend or a dedicated authorization system[2][7].

  • Validate every token on every request.

    • Always check signature validity, expiration (exp claim), audience (aud), issuer (iss), and intended use of the token[1][3][7].
    • Never trust token claims without verification by the server[1][7].
  • Avoid passing tokens in URLs. Never place JWTs in query strings or URL fragments, as they can leak via logs, browser history, or referrer headers[1][10].

  • Rotate keys periodically. Update signing keys frequently and allow a short overlap period to prevent user lockout during key transitions[4].

  • Separate tokens by use. Prefer using different secrets/keys for access, refresh, and ID tokens, and make sure only the correct systems can sign and verify them[1][5].

Example Secure Flow:

  1. After OAuth login, the backend issues a JWT access token (plus a refresh token, if applicable).
  2. The backend transmits the JWT to the client via HTTPS.
  3. The client stores the access token in memory or a secure, short-lived store (e.g., an HttpOnly cookie for web apps).
  4. The client includes the JWT in the Authorization: Bearer <token> header for subsequent API requests.
  5. When the access token expires, the client uses the refresh token (kept securely) to obtain a new access token.
  6. The backend/authorization server validates each token on every request.

Caveats:

  • Storing tokens on the client always carries some risk. XSS attacks can expose tokens in localStorage/sessionStorage. Cookies help but may be vulnerable to CSRF if not configured with SameSite=Strict[1][6].
  • Never use JWTs as persistent sessions. Use them as short-lived proofs of authentication/authorization only[1][2].
  • If handling sensitive data or operating in environments with serious security requirements (e.g., banking apps), follow stricter controls such as avoiding direct JWT storage and using opaque tokens with backend introspection[6].

By following these practices, you minimize both the risk of token compromise and the potential damage if a token is stolen or misused.

Citations:


보안 우려: Access Token URL 노출 방지 필요

Access Token을 쿼리 파라미터로 전달하면 서버 로그, 브라우저 히스토리, Referer 헤더 등에 토큰이 노출될 수 있습니다. 아래와 같이 안전한 전달 방법으로 변경하세요.

수정 필요 위치

  • src/main/java/project/flipnote/auth/controller/OAuthController.java:104

권장 대체 방안

  • HttpOnly, Secure 속성의 쿠키에 토큰 저장
  • 클라이언트가 Authorization: Bearer <token> 헤더로 전송
  • OAuth 임시 인가 코드(authorization code) 발급 후 클라이언트에서 토큰 교환

예시 패치

- location = buildLoginSuccessRedirectUri(tokenPair.accessToken());
+ // 1) 쿠키에 저장하고 리디렉션 URI에는 토큰을 포함하지 않음
+ String redirectUri = buildLoginSuccessRedirectUri(null);
+ ResponseCookie cookie = ResponseCookie.from("access_token", tokenPair.accessToken())
+     .httpOnly(true).secure(true).path("/").maxAge(accessTokenTtl).build();
+ response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
+ location = redirectUri;

—or—

- location = buildLoginSuccessRedirectUri(tokenPair.accessToken());
+ // 2) URL fragment 방식을 사용해 쿼리 파라미터에 노출되지 않도록 변경
+ String fragment = "#access_token=" + tokenPair.accessToken();
+ location = buildLoginSuccessRedirectUri(fragment);

위 중 하나를 선택해 구현하시고, 클라이언트에서도 해당 방식에 맞춰 토큰을 안전하게 처리하도록 변경해 주세요.

🤖 Prompt for AI Agents
In src/main/java/project/flipnote/auth/controller/OAuthController.java at line
104, the access token is currently being included in the URL query parameters,
which exposes it to security risks. To fix this, remove the token from the URL
and instead store it securely in an HttpOnly, Secure cookie set by the server,
or implement an OAuth authorization code flow where the client exchanges a
temporary code for the token. Update the server code to set the token in a
secure cookie or issue an authorization code, and ensure the client is modified
to send the token in the Authorization header or handle the code exchange
accordingly.

@dungbik dungbik merged commit 07329d8 into develop Jul 28, 2025
2 of 3 checks passed
@dungbik dungbik deleted the feat/social-login branch July 28, 2025 06:30
@coderabbitai coderabbitai bot mentioned this pull request Sep 24, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants