Web Push notifications is a web browser messaging protocol defined by the W3C.

Discussions of this interesting technology are clouded because of a terminological
morass.

To understand how Web Push operates, we need to observe that are three (and
potentially four) parties involved. These are:

1) The user's web browser. Let's call that BROWSER
2) The Web Push Service Provider which is operated by the organization controlling the
web browser's source code. Here named PROVIDER. An example of a PROVIDER is
FCM (Firebase Cloud Messaging) which is owned by Google.
3) The Web Application that a user is visiting from their web browser. Let's call this the
SERVICE (short for Web Push application service)
[4) A Custom Web Push Intermediary Service, either third party or self-hosted. Called
INTERMEDIARY here.]

The workflow works like this:

BROWSER visits a website which has a SERVICE.

The SERVICE asks BROWSER for its permission to subscribe to messages coming
from the SERVICE.

Provide context and obtain explicit permission before prompting for notification permission:
It is recommended to set up to a two-step opt-in process where the user is first presented
with a pre-permission dialog box that explains what the notifications are for and why they
are useful. This may help reduce the possibility of users clicking "don't allow.

In Typescript, we can activate a browser's permission dialogue in this manner:

function askPermission(): Promise<NotificationPermission> {
  return new Promise(function(resolve, reject) {
    const permissionResult = Notification.requestPermission(function(result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function(permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
    return permissionResult;
  });
}

If the user grants permission, the client application registers a service worker using
the ServiceWorkerRegistration API.


Here's a version which can be used for testing locally.  Note there can be
caching issues in your browser!  Incognito is highly recommended.

self.addEventListener('push', function(event: PushEvent) {
  console.log('Received a push message', event);

  const title = 'Push message';
  const body = 'The message body';
  const icon = '/images/icon-192x192.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );
});


vue.config.js

module.exports = {
  pwa: {
    workboxOptions: {
      importScripts: ['sw-dev.ts']
    }
  }
}


In the next step, BROWSER requests a data structure from SERVICE called a VAPID (Voluntary
Application Server Identification) which is the public key from a key-pair.

The VAPID is a specification used to identify the application server (i.e. the SERVICE
server) that is sending push messages to a push service. It's an authentication
mechanism that allows the server to demonstrate its identity to the push service, by use
of a public and private key pair. These keys are used by the SERVICE in encrypting
messages being sent to the BROWSER, as well as being used by the BROWSER in
decrypting the messages coming from the SERVICE.

If the BROWSER accepts and grants permission to subscribe to receiving from the
SERVICE Web Push messages, then the BROWSER makes a subscription request to
PROVIDER which creates and stores a special URL for that BROWSER.

const applicationServerKey = urlBase64ToUint8Array('BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U');
const options: PushSubscriptionOptions = {
  userVisibleOnly: true,
  applicationServerKey: applicationServerKey
};

registration.pushManager.subscribe(options)
  .then(function(subscription) {
    console.log('Push subscription successful:', subscription);
  })
  .catch(function(error) {
    console.error('Push subscription failed:', error);
  });

In this example, the `applicationServerKey` variable contains the VAPID public key,
which is converted to a Uint8Array using the `urlBase64ToUint8Array()` function from the
convert-vapid-public-key package. The options object is of type PushSubscriptionOptions,
which includes the `userVisibleOnly` and `applicationServerKey` (ie VAPID public key)
properties. The subscribe() method returns a `Promise` that resolves to a `PushSubscription`
object containing details of the subscription, such as the endpoint URL and the public key.

The VAPID (Voluntary Application Server Identification) key provides more security and
authenticity for web push notifications in the following ways:

    Identifying the Application Server:
    
        The VAPID key is used to identify the application server that is sending the push notifications.
        This ensures that the push notifications are authentic and not sent by a malicious third party.
	
    Encrypting the Messages:
    
        The VAPID key is used to sign the push notifications sent by the application server,
	ensuring that they are not tampered with during transmission. This provides an additional
	layer of security and authenticity for the push notifications.

Adding Contact Information:

    The VAPID key allows a web application to add contact information to the push messages sent to the browser push service.
    This enables the push service to contact the application server in case of need or provide additional debug information about the push messages.

Improving Delivery Rates:

    Using the VAPID key can help improve the overall performance of web push notifications, specifically improving delivery rates.
    By streamlining the delivery process, the chance of delivery errors along the way is lessened.


The PROVIDER sends this URL back to the BROWSER. The BROWSER will then use that
URL to check for incoming messages by way of a special software named a "service
worker". The BROWSER also sends this URL back to SERVICE which will use that
URL to send messages to the BROWSER via the PROVIDER.

Ultimately, the actual process of receiving messages varies from BROWSER to
BROWSER. Approaches vary from long-polling HTTP connections to WebSockets. A
lot of handwaving and voodoo magic. The bottom line is that the BROWSER itself
manages the connection to the PROVIDER whilst the SERVICE must send messages
via the PROVIDER so that they reach the BROWSER.

Now to address the issue of receiving notification messages on mobile devices.
It should be noted that Web Push messages are only received when BROWSER is
open, except in the cases of Chrome and Firefox mobile BROWSERS. In iOS the
mobile application (in our case a PWA) must be added to the Home Screen and
permissions must be explicitly granted that allow the application to receive push
notifications. Further, with an iOS device the user must enable wake on notification to
have their device light-up when it receives a notification (https://support.apple.com/enus/HT208081).

So what about #4? - The INTERMEDIARY. Well, It is possible under very special
circumstances to create your own Web Push PROVIDER. The only case I've found so
far relates to making an Android Custom ROM. (An Android Custom ROM is a
customized version of the Android Operating System.) There are open source
IMTERMEDIARY products such as UnifiedPush (https://unifiedpush.org/) which can
fulfill this role. If you are using iOS you are not permitted to make or use your own
custom Web Push PROVIDER. Apple will never allow anyone to do that. Apple has
none of its own.

It is, however, possible to have a sort of proxy working between your SERVICE and
FCM (or iOS). Services that mash up various Push notification services (like
OneSignal) can perform in the role of such proxies.

#4 -The INTERMEDIARY- doesn't appear to be anything we should be spending our
time on.
