4

I implemented web push notifications using service worker. I collected user subscriptions with a particular application server key. Suppose if we change the application server key, then when we get the subscription using "reg.pushManager.getSubscription()", we will get the old subscription information which was created using the old application server key. How to handle this scenario? How to get the new subscription from the user?

Stanly
  • 663
  • 1
  • 9
  • 26

4 Answers4

7

Get the subscription using reg.pushManager.getSubscription() and check whether current subscription uses the new application server key. If not, then call unsubscribe() function on the existing subscription and resubscribe again.

Community
  • 1
  • 1
Stanly
  • 663
  • 1
  • 9
  • 26
5

After properly starting the service worker and getting the permissions, call navigator.serviceWorker.ready in order to get access to the *.pushManager object.

From this object we call another promise to get the pushSubscription object we actually care about.

If the user was never subscribed pushSubscription will be null otherwise we get the key from it and check if it's different, if that's the case we unsubscribe the user and subscribe them again.

var NEW_PUBLIC_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
    
Notification.requestPermission(function (result) {
    if (permissionResult == 'granted'){
        subscribeUser();
    }
});
    
function subscribeUser() {
    navigator.serviceWorker.ready
    .then(registration => {
        registration.pushManager.getSubscription()
        .then(pushSubscription => {
            if(!pushSubscription){
                //the user was never subscribed
                subscribe(registration);
            }
            else{
                //check if user was subscribed with a different key
                let json = pushSubscription.toJSON();
                let public_key = json.keys.p256dh;
                
                console.log(public_key);
                
                if(public_key != NEW_PUBLIC_KEY){
                    pushSubscription.unsubscribe().then(successful => {
                        // You've successfully unsubscribed
                        subscribe(registration);
                    }).catch(e => {
                        // Unsubscription failed
                    })
                }
            }
        });
    })
}

function subscribe(registration){
    registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(NEW_PUBLIC_KEY)
    })
    .then(pushSubscription => {
        //successfully subscribed to push
        //save it to your DB etc....
    });
}

function urlBase64ToUint8Array(base64String) {
    var padding = '='.repeat((4 - base64String.length % 4) % 4);
    var base64 = (base64String + padding)
        .replace(/\-/g, '+')
        .replace(/_/g, '/');

    var rawData = window.atob(base64);
    var outputArray = new Uint8Array(rawData.length);

    for (var i = 0; i < rawData.length; ++i) {
        outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
}
Viaativa
  • 51
  • 1
  • 2
0

In my case, I managed to solve it by clearing the cache and cookies

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 12 '21 at 19:27
0

The key you get from calling sub.getKey('p256dh') (or sub.toJSON.keys.p256dh) is the client's public key, it will always be different from the server public key. You need to compare the new public server key and sub.options.applicationServerKey.

sub above is the resolved promise from reg.pushManager.getSubscription().

Therefore:

  1. Get PushSubscription interface - reg.pushManager.getSubscription().then(sub => {...}), if sub is null no subscription exists, therefore no worry, but if it's defined:
  2. Inside the block get the current key in use sub.options.applicationServerKey
  3. Convert it to string, because you can't compare ArrayBuffer directly - const curKey = btoa(String.fromCharCode.apply(null, new Uint8Array(sub.options.applicationServerKey)))
  4. Compare it with your new key. If the keys are different call sub.unsubscribe() and then subscribe again by calling reg.pushManager.subscribe(subscribeOptions), where subscribeOptions uses your new key. You call 'unsubscribe' on PushSubscription, but subscribe on PushManager