Skip to content

fix: 웹 소셜 로그인 시 deviceToken 없이도 로그인 가능하도록 수정 - #328

Open
rdyjun wants to merge 2 commits into
mainfrom
fix/web-social-login-device-token
Open

fix: 웹 소셜 로그인 시 deviceToken 없이도 로그인 가능하도록 수정#328
rdyjun wants to merge 2 commits into
mainfrom
fix/web-social-login-device-token

Conversation

@rdyjun

@rdyjun rdyjun commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • SocialLoginRequest.deviceToken@NotBlank 제거 — 웹은 로그인 전 FCM 토큰이 없으므로 nullable 허용
  • acceptLogin 시그니처를 String deviceTokenLong deviceId로 변경 — 컨트롤러에서 device 생성 직후 반환된 deviceId를 JWT에 직접 포함
  • bindOrCreateDeviceLong deviceId를 반환하도록 수정
  • OAuth2ServiceImpl의 불필요한 resolveDeviceId, MemberDeviceRepository 의존성 제거

문제

웹 소셜 로그인 시 FCM 토큰(deviceToken)이 없어 @NotBlank 검증 실패로 로그인 불가.
기존 구조는 deviceToken으로 deviceId를 조회했기 때문에 토큰이 없으면 JWT에 deviceId가 포함되지 않아 블랙리스트 검사도 우회됐음.

동작 방식

  • WEB: deviceToken 없이 요청 → createAndBindWebDevice로 device 행 생성(null 토큰) → 반환된 deviceId를 JWT에 포함
  • 모바일: 기존과 동일하게 FCM 토큰 전송 → bindDeviceWithMemberId로 바인딩 → deviceId 반환

Test plan

  • 웹 소셜 로그인 시 deviceToken 없이 요청해도 로그인 성공 확인
  • 모바일 소셜 로그인 시 기존처럼 deviceToken 포함 요청 정상 동작 확인
  • 발급된 JWT에 deviceId 포함 여부 확인 (블랙리스트 검사 정상 동작)

🤖 Generated with Claude Code

Summary by CodeRabbit

릴리스 노트

  • 개선 사항
    • 기기 바인딩 및 소셜 로그인 프로세스의 내부 처리 방식을 개선했습니다.
    • 기기 관리 시스템의 안정성을 향상시켰습니다.
    • OAuth2 인증 흐름을 최적화하여 더욱 안정적인 기기 추적이 가능하도록 개선했습니다.

- SocialLoginRequest.deviceToken의 @notblank 제거 (nullable 허용)
- acceptLogin 시그니처를 deviceToken → deviceId로 변경하여 JWT에 deviceId 직접 포함
- bindOrCreateDevice가 deviceId를 반환하도록 수정
- WEB 타입은 deviceToken 없이 device 행 생성 후 deviceId를 JWT에 포함

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@rdyjun has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 55 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b457a43a-242a-440b-928d-7661701532cc

📥 Commits

Reviewing files that changed from the base of the PR and between a9a9e8c and 680ac1c.

📒 Files selected for processing (3)
  • src/main/java/com/dongsoop/dongsoop/oauth/controller/OAuth2Controller.java
  • src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2Service.java
  • src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2ServiceImpl.java
📝 Walkthrough

개요

OAuth2 소셜 로그인 흐름에서 디바이스 토큰 기반 처리를 디바이스 ID 기반 처리로 변경합니다. MemberDeviceService.bindDeviceWithMemberId는 이제 바인딩된 디바이스의 ID를 반환하고, OAuth2 컨트롤러 및 서비스는 이 ID를 직접 사용합니다.

변경 사항

디바이스 바인딩 및 ID 반환

Layer / File(s) Summary
디바이스 바인딩 반환 타입 변경
src/main/java/com/dongsoop/dongsoop/memberdevice/service/MemberDeviceService.java, src/main/java/com/dongsoop/dongsoop/memberdevice/service/MemberDeviceServiceImpl.java
bindDeviceWithMemberId 메서드의 반환 타입이 void에서 Long으로 변경되었으며, 구현에서 바인딩된 디바이스의 ID를 반환합니다.

OAuth2 로그인 흐름 리팩토링

Layer / File(s) Summary
OAuth2 컨트롤러 디바이스 ID 통합
src/main/java/com/dongsoop/dongsoop/oauth/controller/OAuth2Controller.java
bindOrCreateDevice 헬퍼 메서드가 Long 디바이스 ID를 반환하도록 변경되었습니다. Kakao, Google, Apple 로그인 엔드포인트에서 이 ID를 oAuth2Service.acceptLogin에 전달하며, 비-WEB 디바이스의 경우 익명 FCM 토픽 구독을 해제합니다.
OAuth2 서비스 시그니처 및 구현 업데이트
src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2Service.java, src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2ServiceImpl.java
acceptLogin 메서드의 세 번째 파라미터가 String deviceToken에서 Long deviceId로 변경되었습니다. 토큰 생성 시 디바이스 리포지토리를 통한 ID 해석 로직이 제거되고 제공된 deviceId를 직접 사용합니다.
요청 DTO 유효성 검사 완화
src/main/java/com/dongsoop/dongsoop/oauth/dto/SocialLoginRequest.java
deviceToken 필드에서 @NotBlank 유효성 검사 애노테이션이 제거되었습니다.

코드 검토 난이도

🎯 3 (중간) | ⏱️ ~20분

변경 사항이 여러 파일에 걸쳐 일관되게 적용되었으나, 로직 자체는 명확하며 (디바이스 토큰에서 디바이스 ID로의 전환), 메서드 시그니처 변경이 관련 호출 사이트를 통해 전파되는 중간 규모의 리팩토링입니다.

관련된 가능성 있는 PR

  • dongsooop/backend#324: 동일한 MemberDeviceServiceImpl.bindDeviceWithMemberId(...) 메서드를 수정하며, 이 PR은 반환 타입을 변경하여 device.getId()를 반환합니다.
  • dongsooop/backend#279: OAuth2 로그인 흐름의 익명 FCM 토픽 구독 해제 로직이 OAuth2Controller에서 동일하게 변경되는 부분과 중복됩니다.
  • dongsooop/backend#316: OAuth2ControllerOAuth2Service.acceptLoginString deviceToken에서 Long deviceId로 변경하는 동일한 OAuth 변경이 있습니다.

제안 레이블

bug

제안 검토자

  • alpin87

🐰 기기 ID로 재탄생한 로그인,
토큰의 옛 길은 이제 잊고,
빠르고 명확한 흐름으로,
디바이스는 귀가로 식별되네!
깔끔한 바인딩, 새로운 시작 ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 요약하고 있습니다. 웹 소셜 로그인 시 deviceToken 없이 로그인 가능하도록 수정한 내용이 정확히 반영되어 있습니다.
Description check ✅ Passed PR 설명이 대부분 완전하나 템플릿의 필수 섹션 중 '관련 이슈' 섹션이 누락되어 있습니다. 나머지 섹션들은 충분히 작성되었습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/web-social-login-device-token

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/dongsoop/dongsoop/oauth/controller/OAuth2Controller.java`:
- Around line 180-187: The mobile branch of bindOrCreateDevice currently calls
memberDeviceService.bindDeviceWithMemberId and unsubscribeAnonymous even when
deviceToken is null/blank; update bindOrCreateDevice to validate deviceToken for
non-WEB deviceType and immediately reject with a 4xx (e.g., throw a
BadRequest-like exception) when deviceToken is null or blank, while keeping the
WEB branch behavior that normalizes blank to null and calls
createAndBindWebDevice; ensure the check happens before calling
memberDeviceService.bindDeviceWithMemberId and unsubscribeAnonymous to prevent
null-token lookups and subsequent NPEs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 51388974-2fde-448b-ad90-7087ef18427b

📥 Commits

Reviewing files that changed from the base of the PR and between 488977d and a9a9e8c.

📒 Files selected for processing (6)
  • src/main/java/com/dongsoop/dongsoop/memberdevice/service/MemberDeviceService.java
  • src/main/java/com/dongsoop/dongsoop/memberdevice/service/MemberDeviceServiceImpl.java
  • src/main/java/com/dongsoop/dongsoop/oauth/controller/OAuth2Controller.java
  • src/main/java/com/dongsoop/dongsoop/oauth/dto/SocialLoginRequest.java
  • src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2Service.java
  • src/main/java/com/dongsoop/dongsoop/oauth/service/OAuth2ServiceImpl.java
💤 Files with no reviewable changes (1)
  • src/main/java/com/dongsoop/dongsoop/oauth/dto/SocialLoginRequest.java

Comment on lines +180 to +187
private Long bindOrCreateDevice(Long memberId, String deviceToken, MemberDeviceType deviceType) {
if (deviceType == MemberDeviceType.WEB) {
memberDeviceService.createAndBindWebDevice(memberId, deviceToken);
} else {
memberDeviceService.bindDeviceWithMemberId(memberId, deviceToken);
unsubscribeAnonymous(deviceToken);
return memberDeviceService.createAndBindWebDevice(memberId, deviceToken);
}

Long deviceId = memberDeviceService.bindDeviceWithMemberId(memberId, deviceToken);
unsubscribeAnonymous(deviceToken);
return deviceId;

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 | 🟠 Major | ⚡ Quick win

모바일 분기에서 deviceToken 누락을 바로 차단해 주세요.

deviceToken이 nullable로 바뀌었는데, non-WEB 요청에도 여기서 그대로 bindDeviceWithMemberId()unsubscribeAnonymous()를 호출합니다. 이 상태면 모바일 요청이 null/blank 토큰으로 들어올 때 findByDeviceToken(null)가 WEB의 null-token 행과 충돌하거나 잘못된 기기를 바인딩할 수 있고, 이어서 List.of(deviceToken)에서 NPE도 납니다. WEB은 blank를 null로 정규화하고, MOBILE은 토큰이 비어 있으면 바로 4xx로 막는 쪽이 안전합니다.

예시 수정
 private Long bindOrCreateDevice(Long memberId, String deviceToken, MemberDeviceType deviceType) {
     if (deviceType == MemberDeviceType.WEB) {
-        return memberDeviceService.createAndBindWebDevice(memberId, deviceToken);
+        String normalizedToken = (deviceToken == null || deviceToken.isBlank()) ? null : deviceToken;
+        return memberDeviceService.createAndBindWebDevice(memberId, normalizedToken);
     }
 
+    if (deviceToken == null || deviceToken.isBlank()) {
+        throw new IllegalArgumentException("모바일 로그인에는 deviceToken이 필요합니다.");
+    }
+
     Long deviceId = memberDeviceService.bindDeviceWithMemberId(memberId, deviceToken);
     unsubscribeAnonymous(deviceToken);
     return deviceId;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/dongsoop/dongsoop/oauth/controller/OAuth2Controller.java`
around lines 180 - 187, The mobile branch of bindOrCreateDevice currently calls
memberDeviceService.bindDeviceWithMemberId and unsubscribeAnonymous even when
deviceToken is null/blank; update bindOrCreateDevice to validate deviceToken for
non-WEB deviceType and immediately reject with a 4xx (e.g., throw a
BadRequest-like exception) when deviceToken is null or blank, while keeping the
WEB branch behavior that normalizes blank to null and calls
createAndBindWebDevice; ensure the check happens before calling
memberDeviceService.bindDeviceWithMemberId and unsubscribeAnonymous to prevent
null-token lookups and subsequent NPEs.

- SocialLoginRequest.deviceToken의 @notblank 제거 (nullable 허용)
- WEB 타입은 createAndBindWebDevice로 device 행 생성 후 반환된 deviceId를 JWT에 직접 포함
- 모바일은 기존 resolveDeviceId 흐름 유지
- acceptLoginWithDeviceId 메서드 추가로 WEB 전용 로그인 처리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant