"Lost" push subscriptions for iOS PWA

Ah ha this explains some of the questions I'm getting re notifications.
You should just be able to enable push notifications again in your preferences.
Ah ha! This explains some of the questions I'm getting re notifications.

When this occurs do push notifications show in preferences as enabled or disabled?
In Preferences do you have to disable then enable and then save to reset?
 
I was able to pinpoint another place that push notifications are being forcibly unsubscribed when they shouldn't be... When a user needs to redo their two-step authentication, the two-step page has no XF.config.userId set, so the browser (and PWA) is told to unsubscribe from push notifications.

JavaScript:
if (XF.config.userId && isExpectedServerKey(subscription))
{
    XF.Push.updateUserSubscription(subscription, 'update');
}
else
{
    subscription.unsubscribe();
    XF.Push.updateUserSubscription(subscription, 'unsubscribe');
}

The browser/PWA is left in a state where Notification.permission = "granted" but XF.Push.isSubscribed = false. And you can't programmatically resubscribe because the browser only allows a push subscription to "start" (since it was killed on the two-step page) with a user gesture.

Code:
XF.Push.handleSubscribeAction(true);

>> Notification prompting can only be done from a user gesture.

TL;DR: Push notification permissions are completely wiped out when the user needs to do a two-step auth (after 30 days by default).
 
So a little more digging today on the lost push notifications. It does appear that push subscriptions are lost for Safari (desktop version as well as PWA) due the above (more specifically, Safari requires a user gesture to re-enable push notifications even if Notification.permission = "granted" already). Chrome (probably other browsers too, but I'm only testing with Chrome and Safari right now) does not require a user gesture to re-enable push notifications.

So in the case where a two-step auth (maybe other places as well) unsubscribes from notifications, only to resubscribe later, that mechanism works in Chrome, but not Safari.

You can test this by going to browser console and running:
JavaScript:
XF.PWA.getRegistration()
.then(function(registration)
{
    return registration.pushManager.getSubscription();
})
.then(function(subscription)
{
    subscription.unsubscribe();
});

Reload the page and then run this in the browser console:
JavaScript:
XF.Push.handleSubscribeAction(true);

...in Chrome the push subscription was re-enabled and you can see the request being made to misc/update-push-subscription on the Network tab.

Do the exact same thing in Safari (desktop or PWA) and you are met with an error:
Code:
Notification prompting can only be done from a user gesture.
Permission was not granted



TL;DR: Safari/PWA requires a user gesture to subscribe to a push notification, even if permission was already granted.
 
Rather than XF.Push.handleSubscribeAction() forcing Notification.requestPermission(), instead check if the browser has a subscription, and simply use that if there's already one in place for the browser. That should work for all browsers.

JavaScript:
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
  serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) {
    if (subscription) {
        // Browser has a subscription already, use it.
        console.log(JSON.stringify(subscription));
        XF.Push.updateUserSubscription(subscription, 'insert');
        XF.Push.isSubscribed = true;
    } else {
       // go the Notification.requestPermission() route...
    }
  });
});

That doesn't work in itself because XenForo killed the push subscription in the browser if the user visited a page where they weren't fully logged in (like two-step re-auth). The better option I think might be to not rely on the browser to be able to reactivate a subscription after it's been unsubscribed due to log out (since Safari/PWA requires a user gesture), and instead have a xf_user_push_subscription.push_state column where the unsubscribe action toggles that between active/disabled to prevent push notifications from being sent to a browser where the user "logged out". Then it can be reactivated programmatically without relying on the browser (and sometimes needing a user gesture).
 
Last edited:
This update addresses iOS PWA losing push subscriptions when the user is asked to re-authenticate via two-step. It's still not an ideal fix... the real fix I think is to not rely on a browser's willingness to re-subscribe to push without a user gesture. Right now it's only Safari that requires a user gesture, but I can see this changing with other browsers as it's a better thing to do from a security standpoint. Keeping the push subscription in the database and toggling it on/off on XenForo's side is going to be a much more reliable way to handle it.

FWIW, XenForo unsubscribing a user from push notifications when they go into the admin area is also going to cause lost push subscriptions for Safari/iOS PWA users in default XenForo setup (addon already addressed that one previously).
 
Last edited:
Top Bottom