Skip to content

fix(webpush): resolve push subscription bugs for multi-device and shared browsers#3142

Open
fallenbagel wants to merge 2 commits into
developfrom
fix/webpush-notifications
Open

fix(webpush): resolve push subscription bugs for multi-device and shared browsers#3142
fallenbagel wants to merge 2 commits into
developfrom
fix/webpush-notifications

Conversation

@fallenbagel

@fallenbagel fallenbagel commented Jun 9, 2026

Copy link
Copy Markdown
Member

Description

This PR fixes a cluster of web push notification bugs affecting non-admin users, multi-device setups, and shared browsers.

Note

The changes are split into separate commits so they remain individually bisectable after the rebase merge. We should rebase merge rather than squash so commit boundaries are preserved on develop.

Previously, cleanup matched only on userAgent, which meant two devices of the same model and OS/browser version sharing an identical user agent string were treated as the same device. Registering on the second device could delete the first device's subscription, breaking its notifications.

The cleanup now also requires a matching auth key, so only a true push-service endpoint rotation, same subscription identity with a new endpoint, is cleaned up. Duplicate detection remains endpoint-based so same-auth endpoint rotations can still replace the stale endpoint. Sibling devices that share a user agent but have distinct subscriptions are left intact. Any subscription that becomes genuinely invalid is still pruned by the delivery path, which removes subscriptions on a 410 or 404 response per RFC 8030.

The second commit addresses cross-user contamination on shared browsers, where browser-level push subscriptions are per-origin and can otherwise persist across logout/login transitions.
Logout now unsubscribes the browser-level push subscription, deletes that endpoint from the current user's backend records, and clears the local enablement flag. This prevents a later user on the same browser from inheriting the previous user's push state.

The verification routine no longer treats the mere presence of any browser subscription as proof that the current user is subscribed. It verifies ownership against the current user's records. The resubscribe routine no longer bails out when a browser subscription exists but does not belong to the current user; it tears down the orphaned browser subscription and registers a fresh one. The disable action also verifies ownership before unsubscribing or deleting, so a user disabling push on a shared browser can no longer silently kill another user's working subscription.

How Has This Been Tested?

  • Verified one desktop browser, one iPhone on iOS 26, and one iPhone on iOS 18 can all be subscribed as 0xsysr3ll and receive notifications independently.
  • Verified disabling and re-enabling web push on each device does not remove or break sibling device subscriptions.
  • Verified two subscriptions with the exact same userAgent but different push subscription keys can coexist in the database.
  • Verified shared-browser logout unsubscribes the browser subscription and removes the backend row for the logged-in user.
  • Verified logging in as a second user on the same browser does not inherit 0xsysr3ll's push state.
  • Verified switching back to 0xsysr3ll does not show a stale browser subscription while the iPhone subscriptions remain intact.

Screenshots / Logs (if applicable)

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • New Features

    • Push notifications are now unsubscribed during logout and local push settings are cleared.
    • Enabling push uses a verify-and-resubscribe flow for more reliable registration.
  • Bug Fixes

    • Prevents accidental deletion of other users' subscriptions in shared-browser scenarios.
    • Tighter cleanup of stale subscriptions and more robust resubscribe/error handling.
    • Simplified verification of push-enabled state for consistent enable/disable behavior.

@fallenbagel fallenbagel changed the title Fix/webpush notifications ix(webpush): resolve push subscription bugs for non-admins, multi-device, and shared browsers Jun 9, 2026
@fallenbagel fallenbagel changed the title ix(webpush): resolve push subscription bugs for non-admins, multi-device, and shared browsers fix(webpush): resolve push subscription bugs for non-admins, multi-device, and shared browsers Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5291df12-8899-431c-87fa-12e5e652eb69

📥 Commits

Reviewing files that changed from the base of the PR and between 5f4b19e and 15f0d14.

📒 Files selected for processing (4)
  • server/routes/user/index.ts
  • src/components/Layout/UserDropdown/index.tsx
  • src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
  • src/utils/pushSubscriptionHelpers.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
  • src/components/Layout/UserDropdown/index.tsx
  • src/utils/pushSubscriptionHelpers.ts

📝 Walkthrough

Walkthrough

This PR isolates push subscriptions per endpoint, tightens stale-subscription cleanup to auth-scoped deletions, verifies ownership during enable/disable and resubscribe flows, and adds best-effort push cleanup during logout.

Changes

Push Subscription Multi-Device Isolation

Layer / File(s) Summary
Backend subscription lookup and stale cleanup
server/routes/user/index.ts
Registration now finds existing subscriptions by endpoint scoped to the current user; stale cleanup runs only when auth is provided and deletes subscriptions with the same auth but a different endpoint.
Push subscription verification and resubscribe helper
src/utils/pushSubscriptionHelpers.ts
verifyAndResubscribePushSubscription now requires navigator.serviceWorker, returns early when registration is disabled, conditionally unsubscribes existing browser subscriptions, subscribes to a new one, and attempts to delete the old endpoint while ignoring deletion errors; failures throw a standardized error.
Web Push enable/disable with ownership verification
src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
Enable uses verifyAndResubscribePushSubscription. Disable verifies the browser subscription belongs to the current user before calling unsubscribeToPushNotifications and before deleting the backend record. Initial verification simplified to verifyPushSubscription; useEffect deps tightened to [user?.id, currentSettings].
Logout push cleanup
src/components/Layout/UserDropdown/index.tsx
Logout now calls unsubscribeToPushNotifications(user?.id) before logout, performs a best-effort axios.delete for the returned endpoint (errors ignored), and clears pushNotificationsEnabled from localStorage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • gauthier-th
  • 0xSysR3ll

Poem

🐰 Hops through the service worker ground,
Each endpoint kept safe and sound.
Ownership checked, old links untied,
Logout clears crumbs you can't hide.
Now every device rings on its own side. 🥕🔔

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: fixing web push subscription bugs for multi-device and shared browser scenarios.
Linked Issues check ✅ Passed Code changes directly address all objectives from issue #2660: multi-device support via endpoint-based duplicate detection [#3142], stale-subscription cleanup improved with auth-matching [#3142], shared-browser handling via logout unsubscription and verification [#3142], and concurrent device subscriptions retained.
Out of Scope Changes check ✅ Passed All changes are scoped to web push notification handling and directly support the stated objectives; no unrelated modifications detected.

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


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.

@fallenbagel fallenbagel force-pushed the fix/webpush-notifications branch from 02c1975 to 8aac087 Compare June 9, 2026 23:39
@0xSysR3ll 0xSysR3ll changed the title fix(webpush): resolve push subscription bugs for non-admins, multi-device, and shared browsers fix(webpush): resolve push subscription bugs for multi-devices and shared browsers Jun 10, 2026
@0xSysR3ll 0xSysR3ll changed the title fix(webpush): resolve push subscription bugs for multi-devices and shared browsers fix(webpush): resolve push subscription bugs for multi-device and shared browsers Jun 10, 2026
@0xSysR3ll 0xSysR3ll force-pushed the fix/webpush-notifications branch from 8aac087 to 5f4b19e Compare June 13, 2026 11:18
@0xSysR3ll 0xSysR3ll marked this pull request as ready for review June 13, 2026 12:09
@0xSysR3ll 0xSysR3ll requested a review from a team as a code owner June 13, 2026 12:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@server/routes/user/index.ts`:
- Around line 286-294: The cleanup currently requires both req.body.userAgent
and req.body.auth and filters by userAgent in transactionalRepo.find, which
skips stale rows when userAgent changed and can cause unique auth conflicts;
change the conditional to run when req.body.auth is present (remove the
dependence on req.body.userAgent) and update the find() where clause used in
transactionalRepo.find to match by auth and user id with endpoint:
Not(req.body.endpoint) (remove the userAgent equality requirement) so stale
subscriptions are removed based on auth regardless of userAgent.

In `@src/utils/pushSubscriptionHelpers.ts`:
- Around line 87-95: The verification step currently treats any false from
verifyPushSubscription() as an explicit "invalid" and unconditionally calls
unsubscribeToPushNotifications() then resubscribes; change
verifyPushSubscription() to return a typed outcome (e.g., enum/union: "valid" |
"invalid" | "verification_error") or make it throw on transient/non-404 errors,
then update the try block to only call unsubscribeToPushNotifications(userId)
and subsequent subscribeToPushNotifications(userId, currentSettings) when the
outcome is explicit "invalid"; for "verification_error" propagate or throw so
the caller can retry instead of tearing down the browser subscription, and keep
downstream ownership/disable checks gated by the explicit "invalid" result.
Ensure references: verifyPushSubscription, unsubscribeToPushNotifications,
subscribeToPushNotifications are updated accordingly.
🪄 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: eaa5c7ec-bf15-4d0e-bb1f-4aba741b8cda

📥 Commits

Reviewing files that changed from the base of the PR and between 784faa9 and 5f4b19e.

📒 Files selected for processing (4)
  • server/routes/user/index.ts
  • src/components/Layout/UserDropdown/index.tsx
  • src/components/UserProfile/UserSettings/UserNotificationSettings/UserNotificationsWebPush/index.tsx
  • src/utils/pushSubscriptionHelpers.ts

Comment thread server/routes/user/index.ts Outdated
Comment thread src/utils/pushSubscriptionHelpers.ts
…ations

Stale cleanup matched only on userAgent, deleting subscriptions from subling devices that share a UA
string (e.g. two iphones of the same model). Now requires matchin auth so only tru iOS endpoint
rotations are cleaned up.

fix #2660
…browsers

Logout now unsubscribes and clears push state; verifyWebPush no longer treats any browser
subscription as proof the current user is enabled; verifyAndResubscribePushSubscription re-registers
orphaned subscriptions instead of bailing; disable path verifies ownership before unsubscribing to
avoid killing a sibling user's working subscription.
@0xSysR3ll 0xSysR3ll force-pushed the fix/webpush-notifications branch from 5f4b19e to 15f0d14 Compare June 13, 2026 17:50
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.

Web Push Notifications: Only one iPhone is allowed to receive push notifications at the same time

2 participants