Skip to content

What happens if ...?

Grace Lee edited this page May 30, 2018 · 31 revisions

Do we get duplicate notifications for Safari web push if we switch between push providers?

This was tested on https://notify.tech switching from PushAssist to OneSignal (testing Safari web push only).

  1. Clear all website data and push permissions for Safari, and restart Safari.

  2. Enable the PushAssist WordPress plugin.

  3. Refresh the site to accept push notifications from PushAssist:

    PushAssist Safari Details:

    • Device Token

      Executing window.safari.pushNotification.permission('web.com.pushassist.push') results in:

      SafariRemoteNotificationPermission {deviceToken: "5A56D0081CEE045184FCCB0610764D89017587EC43E464368D81508DCAE20904", permission: "granted"}

    • Safari Push Package

      • POST request command:

         curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" -H "Cache-Control: no-cache" -H "Postman-Token: 10488b0c-da82-d194-4b06-893bf39e88c4" -d '{
           "subdomain": "onesignal",
           "ipaddress": "50.131.165.48",
           "segments": ""
         }' "https://pushassist.com/api/v1/pushPackages/web.com.pushassist.push"
        
      • Push Package website.json:

         {
             "websiteName": "onesignal",
             "websitePushID": "web.com.pushassist.push",
             "allowedDomains": ["https://onesignal.pushassist.com"],
             "urlFormatString": "https://%@",
             "webServiceURL": "https://pushassist.com/api"
         }
        
  4. Disable PushAssist and enable OneSignal.

  5. Refresh the site to accept push notifications from OneSignal:

    OneSignal Safari Details:

    • Device Token

      Executing window.safari.pushNotification.permission('web.onesignal.auto.115f8fcb-d4e8-44f7-85e0-0f5ff0e5093f') results in:

      SafariRemoteNotificationPermission {deviceToken: "5A56D0081CEE045184FCCB0610764D89017587EC43E464368D81508DCAE20904", permission: "granted"}

      Note: This is the same device token.

    • Safari Push Package

      • POST request command:
      ```
        curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" -H "Cache-Control: no-cache" -H "Postman-Token: a171c2fd-ef31-136f-1f26-8b5fcd04ba4e" -d '{
          "app_id": "c9c4a506-8351-11e5-8d7d-a0369f2d9328"
        }' "https://onesignal.com/api/v1/safari/v1/pushPackages/web.onesignal.auto.115f8fcb-d4e8-44f7-85e0-0f5ff0e5093f"
       ```
      
      • Push Package website.json:

         {
           "websiteName": "OneSignal Test WordPress Blog",
           "websitePushID": "web.onesignal.auto.115f8fcb-d4e8-44f7-85e0-0f5ff0e5093f",
           "allowedDomains": ["http://notify.tech", "http://www.notify.tech", "https://notify.tech", "https://www.notify.tech"],
           "urlFormatString": "https://onesignal.com/sdks/safariNotificationOpened?params=%@",
           "webServiceURL": "https://onesignal.com/api/v1/safari"
         }
        
  6. Notification from OneSignal:

  1. Notification from PushAssist
  • Clicking it links to https://pushassist.com
  • Click is not recorded on the PushAssist dashboard, but this is probably because the notification clicks aren't working for PushAssist (tested clearing all site data and permissions again on Safari and resubscribed, and notification clicks were not recorded for Safari. Tested PushAssist on Chrome and clicks were recorded for Chrome (12-7-2016)).

  1. Interesting to note that, even when subscribing on a completely separate website https://onesignal.com/webpush on Safari, after clearing all data and permissions, the device token is the same:

window.safari.pushNotification.permission('web.onesignal.auto.1b1338f0-ae7a-49c0-a5a1-0112be9b9bea'):

SafariRemoteNotificationPermission {deviceToken: "5A56D0081CEE045184FCCB0610764D89017587EC43E464368D81508DCAE20904", permission: "granted"}

Based on the apsd logs, it seems like the topics are different:

Dec 7 16:52:37 COMPUTER-jpang apsd[77]: <APSCourier: 0x7f99f0e09f60>: Received high priority message for topic 'web.onesignal.auto.115f8fcb-d4e8-44f7-85e0-0f5ff0e5093f' with payload

Dec 7 16:52:15 COMPUTER-jpang apsd[77]: <APSCourier: 0x7f99f0e09f60>: Received high priority message for topic 'web.com.pushassist.push' with payload

In conclusion, it seems like Safari subscription between different push providers requires subscribing, and notifications are treated separately. We also tested switching out OneSignal app with another on a Safari website (with different web IDs), and we were still re-prompted.

Why can't web push subscribers be exported or imported from OneSignal to another push provider?

When our service workers are replaced by another push provider's service workers, after the browser update process forces the other push provider's service workers to be active, the onInstall / onActive method of the other push provider's service workers can unsubscribe from push and resubscribe for push under the other provider. This isn't exactly the same as exporting/importing subscribers since the existing subscribers can't immediately be sent a push message from an other push provider. The browser auto-update takes time (only if the user is connected to the internet, for example).

Using VAPID keys:

Each push subscription is tied to the VAPID public key (applicationServerKey) generated by OneSignal which is public info and can be distributed. When sending a notification, our server signs a JSON Web Token with the app-specific private key -- we have to give this private key to the other push provider. This way, the other push provider can generate messages that are valid. The other push provider must send messages using web push payloads, and in a data format that our service worker expects to receive. Additionally, clicks tracked by our service worker will not go to the other push provider but will instead go to us.

Not using VAPID keys and using legacy GCM Sender ID / Server API Keys:

Each push subscription is tied to the GCM Sender ID defined in manifest.json at the time of subscription. However, even if the other push provider has this GCM Sender ID and Server API Key, sending a message to the subscribed user results in OneSignal's service worker receiving the message, and not the other push provider's service worker. This means the other push provider must send messages using payloads in the data format that OneSignal expects. For more ancient users that use legacy GCM push and do not use payloads, it's not possible for other push providers to send them a message, since our service worker would expect the notification contents to be stored with us (this is how our system worked before payloads were possible).

What fixes are available if a single HTTPS app is used for multiple origins?

Complex way losing the least amount of existing subscribers

Note that HTTP apps don't have this issue since all of its users are subscribed from origin. But for HTTPS sites:

Modify the service worker file to trigger a browser-based update check after 24 hours. As part of the modification, call importScripts() to a special script (to be written) that modifies the app ID in IndexedDB and registers user's existing push subscription to the new app ID.

Because the service worker file has been modified, the browser itself will update the service worker and run our new script, so this is guaranteed to be run eventually, especially if we send another push notification after 24 hours of this modification, since I think browsers also perform their SW update check on a push event.

The script will copy the push subscription details on to a new OneSignal app. It can also remove push subscription details from the existing OneSignal app to show progress and prevent the user from getting another duplicate notification.

Once the new OneSignal app has the push subscription, this is all that's needed to prevent the user from getting duplicate notifications! The push subscription was always valid, we just needed the subscription to live on a separate OneSignal app for each separate origin to prevent 3 separate valid subscriptions pointing to the same actual user from being sent a notification (when sending to all users).

The script must have the other app's OneSignal IDs hardcoded. Something like a "if you, the worker, is running on origin c.example.com, please resubscribe for push on this new app ID c". Hardcoding the information into the script is the only way to prevent another round trip from occurring to fetch new information.

If a user is no longer subscribed, after or on Chrome 55 we can re-subscribe in the SW using PushSubscriptionOptions without needing to fetch the manifest.json gcm_sender_id.

Current More Straightforward Way (loses inactive existing subscribers)

We currently ask clients to simply create new OneSignal apps for each origin and update the app ID for each origin's init script.

The difference between this shorter approach and the above longer approach is that the above approach should theoretically resubscribe all subscribers who have an active service worker.

This current approach ignores existing subscribers and expects active subscribers to eventually revisit the website.

What happens if you switch to HTTPS from a single app (keeping the same app)?

The user's client-side IndexedDb data and push subscription will no longer exist, since the HTTPS site is a different origin. A new user entry will be created for the user, in the same OneSignal app, since there is no user with an existing subscription that matches the newly obtained subscription after the user subscribes.

The user can have previously subscribed to the original HTTP version of the site and then the new HTTPS version of the site, and will receive two notifications if notifications are sent out to all users.

What happens if you change the app ID?

The SDK no longer automatically unsubscribes or modifies IndexedDB data when an App ID is detected to be different from before. This behavior was dangerous, so it was removed.

Instead, for existing users, the user will receive a new user ID, since users are registered on every new session, and the registration now occurs on a different app ID, so OneSignal will create a new user ID since it isn't able to find a previous user with the provided registration token.

The user will keep the same registration token and the same push subscription. This means that for non-working subscriptions, it will not be fixed by creating a new app.

For upgrades from HTTP to HTTPS, the change of origin automatically wipes out all IndexedDB data and push subscription, so users upgrading from HTTP to HTTPS only need to use a new app ID.

Note: In a future SDK update, I think it'd be great if all data could be segmented by app ID, so that switching between app IDs gives users a fresh start (in line with what you'd expect by creating a new app) without erasing previous data tied to a different app ID. Currently, our IndexedDB database only stores one set of information regardless of the app ID.

When do we get an unsubscription event from Google?

Clearing Cookies and other site and plugin data after subscribing, and sending another message

Environment: Chrome 54.0.2840.87 (64-bit) on Mac OS X 10.11.6

Revision: c5ec6c5a12f2ed748bb08c1a8567b3c0df89dccd-refs/branch-heads/2840@{#805}

  1. Subscribe.
  2. Close the tab you subscribed on.
  3. Clear Cookies and other site and plugin data (Settings -> Privacy -> Clear Browsing Data...).
  4. Send yourself a notification.
  5. Observe delivery results are okay.
  6. Send yourself another notification.
  7. Observe delivery results mark you as unsubscribed.

Do we get a unsubscription event right after the first message delivery?

No. It looks like the browser sends an unregistration message though:

Do we get an unsubscription event right after the second message delivery?

Yes. Google immediately tells us the user is no longer subscribed. The browser's chrome://gcm-internals does not record any message received.

Note 1: It looks like clearing Cookies and other site and plugin data is the only setting necessary in clearing browser data to unsubscribe the user (all the other clearing options are unrelated).

Note 2: This method of clearing data also unregisters the service worker itself which means the entry in chrome://serviceworker-internals is missing.