Skip to content

[Feat/#600] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현 - #601

Merged
yangwooyoung123 merged 5 commits into
developfrom
feature/issue-600
Aug 3, 2026
Merged

yangwooyoung123 merged 5 commits into
developfrom
feature/issue-600

Conversation

@yangwooyoung123

@yangwooyoung123 yangwooyoung123 commented Aug 1, 2026 •

Copy link
Copy Markdown
Collaborator

📌 관련 이슈번호

(Closes 키워드가 있어야 PR이 머지되었을 때 이슈가 자동으로 닫힌다)

📌 PR 유형

어떤 변경 사항이 있나요?

  • 새 기능 추가
  • 버그 수정
  • 리팩토링

📌 PR 요약

애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현

📌 작업 세부 내용

  1. 애플 로그인 시 code를 애플 서버에 교환하며 Apple_Refresh_Token 발급 후 Redis에 8일간 저장
  2. 탈퇴 후 7일 경과 시 cron에서 Redis의 Apple_Refresh_Token으로 애플 서버 연동 해제(revoke) API 호출
  3. revoke 완료 후 Redis 키 삭제 및 DB 유저 삭제 처리

📸 스크린샷 (선택)

🔗 참고 자료

Summary by CodeRabbit

  • New Features

    • Improved Apple sign-in by securely processing authorization codes and managing token exchanges.
    • Apple-linked account connections are disconnected when accounts are permanently removed after the retention period.
  • Bug Fixes

    • Improved cleanup of expired Apple-linked accounts, including removal and revocation of stored Apple tokens.
  • Documentation

    • Clarified account withdrawal and Apple integration disconnection behavior.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Apple authentication now exchanges authorization codes for tokens and stores Apple refresh tokens in Redis. Expired-user cleanup revokes those tokens through Apple and removes them. Withdrawal documentation reflects Apple disconnection after seven days.

Changes

Apple token lifecycle

Layer / File(s) Summary
Authorization-code exchange and refresh-token storage
src/controllers/auth.controller.js, src/services/auth.service.js
The Apple callback passes the authorization code to handleAppleLogin. AppleAuthService generates client-secret JWTs, exchanges codes for tokens, stores refresh tokens in Redis with a retention-based TTL, and preserves the token during withdrawal.
Expired-account Apple revocation
src/services/auth.service.js, src/swagger/swagger.yml
Expired-user cleanup retrieves Apple refresh tokens, revokes them through Apple, deletes successful token records, and logs failures as warnings. The API documentation describes Apple integration disconnection after seven days.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AuthCallback
  participant AppleAuthService
  participant AppleTokenEndpoint
  participant Redis
  AuthCallback->>AppleAuthService: submit identity token, user data, and code
  AppleAuthService->>AppleAuthService: generateAppleClientSecret()
  AppleAuthService->>AppleTokenEndpoint: exchange code
  AppleTokenEndpoint-->>AppleAuthService: return refresh token
  AppleAuthService->>Redis: store refresh token with retention TTL
Loading
sequenceDiagram
  participant ExpiredUserCleanup
  participant Redis
  participant AppleAuthService
  participant AppleRevokeEndpoint
  ExpiredUserCleanup->>Redis: retrieve Apple refresh token
  ExpiredUserCleanup->>AppleAuthService: generateAppleClientSecret()
  ExpiredUserCleanup->>AppleRevokeEndpoint: revoke refresh token
  AppleRevokeEndpoint-->>ExpiredUserCleanup: return revocation result
  ExpiredUserCleanup->>Redis: delete token after success
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement delayed Apple token storage, revocation, and cleanup after account withdrawal as required by issue #600.
Out of Scope Changes check ✅ Passed All code and documentation changes support the Apple integration disconnection objective in issue #600.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes disconnecting Apple integration after a defined period following account withdrawal, which matches the main changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-600

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

@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

🧹 Nitpick comments (2)
src/services/auth.service.js (2)

190-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Log the full Apple error response for easier debugging.

The Kakao unlink failure above logs err.response?.data ?? err.message (Line 168), but the Apple revoke failure only logs err.message (Line 191). Apple's error body (for example invalid_grant) is more useful for diagnosing recurring revoke failures than the generic axios message.

🩹 Proposed fix
             } catch(err) {
-              console.warn(`애플 연동 해제 실패 (userId: ${user.id})`, err.message);
+              console.warn(`애플 연동 해제 실패 (userId: ${user.id})`, err.response?.data ?? err.message);
             }
🤖 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/services/auth.service.js` around lines 190 - 192, Update the Apple revoke
failure handler in the catch block to log the full error response body when
available, falling back to err.message otherwise, matching the existing Kakao
unlink logging behavior while preserving the user ID context.

171-195: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid regenerating the Apple client secret per user in the cleanup loop.

new AppleAuthService() and generateAppleClientSecret() run once per Apple user found in users. The client secret does not depend on the user, so this repeats the ES256 signing work needlessly for every user in the same cleanup run. Generate it once per deleteExpiredUsers invocation instead.

♻️ Proposed refactor: generate the client secret once per run
       const tokenKeysMap = new Map();
       let cursor = "0";
+      let appleClientSecretPromise = null;
+      const getAppleClientSecret = () => {
+        if (!appleClientSecretPromise) {
+          appleClientSecretPromise = new AppleAuthService().generateAppleClientSecret();
+        }
+        return appleClientSecretPromise;
+      };
       do {
           if (appleRefreshToken) {
             try {
-              const appleService = new AppleAuthService();
-              const clientSecret = await appleService.generateAppleClientSecret();
+              const clientSecret = await getAppleClientSecret();
               await axios.post(
🤖 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/services/auth.service.js` around lines 171 - 195, Update
deleteExpiredUsers to generate the Apple client secret once before iterating
through users, then reuse that value in the Apple token revocation logic instead
of constructing AppleAuthService and calling generateAppleClientSecret for each
user. Keep the per-user revocation and cleanup behavior unchanged.
🤖 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/services/auth.service.js`:
- Around line 171-195: Update the Apple withdrawal flow in withdrawAppleUser to
reset the Redis key’s TTL when the user withdraws, using the configured
deletion-window duration so the token remains available to the cleanup block
that revokes it. Ensure the related token-storage logic does not expire the key
before that post-withdrawal cleanup window; preserve deletion after successful
revocation.

---

Nitpick comments:
In `@src/services/auth.service.js`:
- Around line 190-192: Update the Apple revoke failure handler in the catch
block to log the full error response body when available, falling back to
err.message otherwise, matching the existing Kakao unlink logging behavior while
preserving the user ID context.
- Around line 171-195: Update deleteExpiredUsers to generate the Apple client
secret once before iterating through users, then reuse that value in the Apple
token revocation logic instead of constructing AppleAuthService and calling
generateAppleClientSecret for each user. Keep the per-user revocation and
cleanup behavior unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 69be9be7-0857-45cc-9c69-51e5c47f219a

📥 Commits

Reviewing files that changed from the base of the PR and between f9b4d36 and 7f008eb.

📒 Files selected for processing (3)
  • src/controllers/auth.controller.js
  • src/services/auth.service.js
  • src/swagger/swagger.yml

Comment thread src/services/auth.service.js Resolved
@coderabbitai coderabbitai Bot mentioned this pull request Aug 1, 2026
3 tasks
@yangwooyoung123 yangwooyoung123 changed the title [Feat/#600] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현 <WIP>[Feat/#600] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현 Aug 1, 2026

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/services/auth.service.js (4)

528-545: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not complete login when refresh-token capture fails.

The catch logs exchange or Redis failures and allows a successful session to return. A response without appleTokens.refresh_token also skips storage without an error. The cleanup job then has no token to revoke. Apple’s authorization-code response includes a refresh token, and the refresh token is required for the revoke flow. (developer.apple.com)

Treat a missing token or Redis write failure as an explicit failure. Alternatively, persist a durable retry state before marking the login complete.

🤖 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/services/auth.service.js` around lines 528 - 545, Update the login flow
around exchangeCodeForTokens and the redis.set call so a missing
appleTokens.refresh_token or any token-storage failure is treated as an explicit
login failure. Do not continue to successful session completion after the catch;
propagate or return the established failure response, ensuring the login is not
marked complete without a stored refresh token.

528-545: 🗄️ Data Integrity & Integration | 🟠 Major

Retain the token until the withdrawal window completes.

This change resets the TTL only when the key still exists. Line 535-540 still starts the TTL at login, so a user who withdraws after (DELETE_AFTER_DAYS + 1) days without logging in loses the key before Line 570 can reset it. Cleanup then cannot revoke the Apple authorization. The separate GET and EXPIRE calls also leave a race if the key expires between them. Keep the token through the full pre-withdrawal period or until successful revocation, and make the reset outcome observable. Apple requires a valid token for revocation. (developer.apple.com)

Also applies to: 569-577

🤖 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/services/auth.service.js` around lines 528 - 545, The Apple refresh-token
persistence in the login flow expires too early and uses a race-prone TTL reset
during withdrawal cleanup. Update the token storage around exchangeCodeForTokens
and the withdrawal cleanup flow to retain the token through the complete
pre-withdrawal window, then atomically preserve or renew it until revocation
succeeds and remove it afterward. Make the Redis renewal result observable and
ensure cleanup can still access a valid token for Apple revocation.

171-195: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add bounded timeouts to both Apple HTTP requests.

Neither auth/token nor auth/revoke has a request-level timeout, and no shared Axios timeout exists. Set a finite timeout and handle timeout errors as retryable failures.

🤖 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/services/auth.service.js` around lines 171 - 195, Add a finite Axios
request timeout to both Apple `auth/token` and `auth/revoke` calls, including
the revoke request in the Apple disconnect flow and the corresponding token
request in its existing implementation. Reuse a shared timeout value where
available, and classify timeout errors as retryable while preserving the current
error handling behavior for other failures.

171-195: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Keep failed Apple revocations retryable before deleting the user.

The daily cleanup cron logs the failure and still deletes user. The Apple refresh token can remain active for the remaining Redis TTL, but no user record remains for a retry. Persist a retryable revocation state or job, and delete the user only after successful revocation or a confirmed terminal state.

🤖 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/services/auth.service.js` around lines 171 - 195, Update the Apple unlink
flow around AppleAuthService, the Apple revoke request, and user deletion so a
revocation failure persists a retryable state or job instead of allowing user
deletion to continue. Delete the user only after successful revocation or an
explicitly confirmed terminal outcome, while preserving the existing Redis token
for retries.
🤖 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/services/auth.service.js`:
- Around line 569-577: Update the withdrawal flow in the surrounding
user-withdrawal method so Redis failures after prisma.user.update do not leave
recovery impossible: make the apple refresh-token TTL restoration idempotently
retryable or enqueue a durable revocation job before returning failure. Preserve
the committed deletedAt state and ensure retries can restore the token TTL
without being blocked by ALREADY_WITHDRAWN.

---

Outside diff comments:
In `@src/services/auth.service.js`:
- Around line 528-545: Update the login flow around exchangeCodeForTokens and
the redis.set call so a missing appleTokens.refresh_token or any token-storage
failure is treated as an explicit login failure. Do not continue to successful
session completion after the catch; propagate or return the established failure
response, ensuring the login is not marked complete without a stored refresh
token.
- Around line 528-545: The Apple refresh-token persistence in the login flow
expires too early and uses a race-prone TTL reset during withdrawal cleanup.
Update the token storage around exchangeCodeForTokens and the withdrawal cleanup
flow to retain the token through the complete pre-withdrawal window, then
atomically preserve or renew it until revocation succeeds and remove it
afterward. Make the Redis renewal result observable and ensure cleanup can still
access a valid token for Apple revocation.
- Around line 171-195: Add a finite Axios request timeout to both Apple
`auth/token` and `auth/revoke` calls, including the revoke request in the Apple
disconnect flow and the corresponding token request in its existing
implementation. Reuse a shared timeout value where available, and classify
timeout errors as retryable while preserving the current error handling behavior
for other failures.
- Around line 171-195: Update the Apple unlink flow around AppleAuthService, the
Apple revoke request, and user deletion so a revocation failure persists a
retryable state or job instead of allowing user deletion to continue. Delete the
user only after successful revocation or an explicitly confirmed terminal
outcome, while preserving the existing Redis token for retries.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 58bc02ee-6d60-4406-b975-542b2ad3183b

📥 Commits

Reviewing files that changed from the base of the PR and between 7f008eb and eca9690.

📒 Files selected for processing (1)
  • src/services/auth.service.js

Comment thread src/services/auth.service.js Resolved
@yangwooyoung123 yangwooyoung123 changed the title <WIP>[Feat/#600] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현 [Feat/#600] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현 Aug 2, 2026
@yangwooyoung123
yangwooyoung123 merged commit 5e8dda4 into develop Aug 3, 2026
3 checks passed
@songwol38
songwol38 deleted the feature/issue-600 branch August 8, 2026 18:56
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.

[Feat] 애플 계정 탈퇴 후 특정 기간 경과 시 Apple 서버 연동 해제 구현

1 participant