fix: use legacy Safari push path only for existing legacy subscribers#1441
fix: use legacy Safari push path only for existing legacy subscribers#1441sherwinski merged 18 commits intomainfrom
Conversation
2c6fed0 to
034ccf0
Compare
|
@fadi-george @abdulraqeeb33 Results of manually-tested scenarios:
|
Safari 16.4+ supports standard Web Push (VAPID), but `window.safari.pushNotification` was never removed. The SDK checked for API existence to decide the push path, which routed ALL macOS Safari users through the legacy flow — even on versions that fully support VAPID. When the legacy push package validation fails (e.g. invalid allowedDomains), this causes the error: "Safari url/icon/certificate invalid or in private mode" Now `useSafariLegacyPush()` checks the actual permission state: only users who have already granted legacy push (with a valid deviceToken) stay on the legacy path. New users on Safari 16.4+ are routed through the standard VAPID flow via `useSafariVapidPush()`.
Cover the new `useSafariLegacyPush()` behavior: - Returns false when window.safari is undefined - Returns false when pushNotification API exists but no safariWebId - Returns false for new users (permission "default", no deviceToken) - Returns false for denied users - Returns true only for existing legacy subscribers (granted + deviceToken) Also tests `getSubscriptionType()` integration to verify legacy subscribers stay on SafariLegacyPush while new users are not routed to the legacy path.
For Safari < 16.4 where VAPID isn't available, returns early so users are routed through the legacy Safari push path. This is the only path that works on those browsers. On Safari 16.4+ the existing logic still applies: only users with an active legacy subscription (granted + deviceToken) use the legacy path.
Existing tests now explicitly mock VAPID support in beforeEach to simulate Safari 16.4+ Adds two new test cases for Safari < 16.4: - useSafariLegacyPush returns true even for new users when VAPID isn't available - getSubscriptionType returns _SafariLegacyPush when VAPID isn't available
2da22a5 to
12b5a32
Compare
|
@claude[agent] review |
Code review complete. The implementation correctly fixes Safari 16.4+ VAPID routing by checking permission state instead of just API presence. Test coverage is comprehensive (10 new tests), manual testing validates all scenarios, and backward compatibility is preserved for existing legacy subscribers. Minor suggestion: consider reusing |
| }; | ||
| }; | ||
|
|
||
| const clearVapidSupport = () => { |
There was a problem hiding this comment.
doesnt seem needed, tests pass without it
There was a problem hiding this comment.
This is what I get:
FAIL src/shared/environment/detect.test.ts > getSubscriptionType > returns SafariLegacyPush on Safari < 16.4 (no VAPID support)
AssertionError: expected 'ChromePush' to be 'SafariLegacyPush' // Object.is equality
Expected: "SafariLegacyPush"
Received: "ChromePush"
| "prepare": "vp config" | ||
| }, | ||
| "dependencies": { | ||
| "@types/jest": "^30.0.0", |
There was a problem hiding this comment.
At the time it was needed for changes to the test files; specifically for importing afterEach and beforeEach
There was a problem hiding this comment.
what do you mean, that comes from vitest
There was a problem hiding this comment.
ran build,test, & lint without it and didnt get any errors
There was a problem hiding this comment.
Shoot, I didn't realize this was added as a dependency. Removing
|
but even legacy safari subscriptions on 16.4+ can be migrated, see |
| @@ -150,10 +150,10 @@ export default defineConfig({ | |||
| server: { | |||
There was a problem hiding this comment.
maybe could do:
const getBooleanEnv = (env?: string) => {
return !!env && env === 'true';
};
do we have to test on any other browser? |
Agreed, let's have the next release address:
|
I included a test for Chrome to show there were no regressions. The issue that this PR addresses only affects Safari. |
| const safariWebId = OneSignal?.config?.safariWebId; | ||
| if (!safariWebId) return false; | ||
|
|
||
| const hasVapidSupport = |
There was a problem hiding this comment.
We already do this in supportsVapidPush so could do:
export const supportsVapidPush =
typeof PushSubscriptionOptions !== 'undefined' &&
// eslint-disable-next-line no-prototype-builtins
PushSubscriptionOptions.prototype.hasOwnProperty('applicationServerKey');
export const useSafariLegacyPush = (): boolean => {
if (!isBrowser() || window.safari?.pushNotification == undefined) {
return false;
}
const safariWebId = OneSignal?.config?.safariWebId;
if (!safariWebId) return false;
if (!supportsVapidPush) return true;
const { permission, deviceToken } = window.safari.pushNotification.permission(safariWebId);
return permission === 'granted' && deviceToken != null;
};
Description
1 Line Summary
Fix Safari 16.4+ to use VAPID push for new users instead of the broken legacy path, while preserving legacy push for existing subscribers and Safari < 16.4.
Details
useSafariLegacyPush()previously returnedtruewheneverwindow.safari.pushNotificationexisted, forcing ALL macOS Safari users onto the legacy push path — including Safari 16.4+ which fully supports standard Web Push (VAPID). This caused new users on modern Safari to hit "Safari url/icon/certificate invalid" errors.Now the function checks the actual subscription state:
permission === 'granted'with a validdeviceToken) stay on the legacy path. New users go through VAPID.PushSubscriptionOptionsis unavailable, so these browsers are routed to the legacy path regardless of subscription state.Systems Affected
Validation
Tests
Info
12 new unit tests covering
useSafariLegacyPush()andgetSubscriptionType()across Safari < 16.4, Safari 16.4+ new users, and existing legacy subscribers.Manual testing performed on:
web.push.apple.comtoken, type =SafariPushmain) correctly detected and kept on legacy path withdeviceTokenpreservedChecklist
Programming Checklist
Interfaces:
Functions:
Typescript:
Other:
elem of arraysyntax. PreferforEachor usemapcontextif possible. Instead, we can pass it to function/constructor so that we don't callOneSignal.contextScreenshots
Info
Safari 15.6 — new user, legacy path (BrowserStack)
Safari 26.2 — new user, VAPID path
Safari 26.2 — existing legacy subscriber preserved
Chrome 146 — no regression
Checklist