Browse Source

Added a bit more of the workflow on the client side

project-map-link
Matthew Aaron Raymer 1 year ago
parent
commit
f1b3094026
  1. 206
      web-push.md

206
web-push.md

@ -13,11 +13,12 @@ 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 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) call this the SERVICE (short for Web Push application service)
[4) A Custom Web Push Intermediary Service, either third party or self-hosted. [4) A Custom Web Push Intermediary Service, either third party or self-hosted.
Called INTERMEDIARY here.] Called INTERMEDIARY here. FCM also may fit in this category if the SERVICE
has an API key from FCM.]
The workflow works like this: The workflow works like this:
BROWSER visits a website which has a SERVICE. BROWSER visits a website which hosts a SERVICE.
The SERVICE asks BROWSER for its permission to subscribe to messages coming The SERVICE asks BROWSER for its permission to subscribe to messages coming
from the SERVICE. from the SERVICE.
@ -25,10 +26,10 @@ from the SERVICE.
The SERVICE will provide context and obtain explicit permission before prompting The SERVICE will provide context and obtain explicit permission before prompting
for notification permission: for notification permission:
In orer to provide this context and explict permission a two-step opt-in process In order to provide this context and explict permission a two-step opt-in process
where the user is first presented with a pre-permission dialog box that explains 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 what the notifications are for and why they are useful. This may help reduce the
possibility of users clicking "don't allow. possibility of users clicking "don't allow".
Now, to explain what happens in Typescript, we can activate a browser's Now, to explain what happens in Typescript, we can activate a browser's
permission dialogue in this manner: permission dialogue in this manner:
@ -52,8 +53,15 @@ function askPermission(): Promise<NotificationPermission> {
} }
``` ```
If the user grants permission, the client application registers a service worker The Notification.permission property indicates the permission level for the
using the `ServiceWorkerRegistration` API. current session and returns one of the following string values:
'granted': The user has granted permission for notifications.
'denied': The user has denied permission for notifications.
'default': The user has not made a choice yet.
Once the user has granted permission, the client application registers a service
worker using the `ServiceWorkerRegistration` API.
The `ServiceWorkerRegistration` API is accessible via the browser's `navigator` The `ServiceWorkerRegistration` API is accessible via the browser's `navigator`
object and the `navigator.serviceWorker` child object and ultimately directly object and the `navigator.serviceWorker` child object and ultimately directly
@ -77,8 +85,8 @@ navigator.serviceWorker.register('sw.js', { scope: '/' })
``` ```
The `sw.js` file contains the logic for what a service worker should do. The `sw.js` file contains the logic for what a service worker should do.
It executes in a separate thread from the web page but provides a means It executes in a separate thread of execution from the web page but provides a
of communicating between itself and the web page via messages. means of communicating between itself and the web page via messages.
Note that there is a scope can specify what network requests it may Note that there is a scope can specify what network requests it may
intercept. intercept.
@ -86,11 +94,11 @@ intercept.
The Vue project already has its own service worker but it is possible to The Vue project already has its own service worker but it is possible to
create multiple service worker files by registering them on different scopes. create multiple service worker files by registering them on different scopes.
It is useful architecturally to specify a separate server worker. It is useful architecturally to specify a separate server worker file.
In the case of web push, the path of the scope only has reference to the domain In the case of web push, the path of the scope only has reference to the domain
of the service worker and no relationship to the pathing for the web push of the service worker and no relationship to the pathing for the web push
server. In order to specify different server workers they need to be on server. In order to specify more than one server workers each needs to be on
different scope paths! different scope paths!
Here's a version which can be used for testing locally. Note there can be Here's a version which can be used for testing locally. Note there can be
@ -126,22 +134,59 @@ module.exports = {
} }
} }
Once we have the service worker registered and the ServiceWorkerRegistration is
returned, we then have access to a `pushManager` property object. This property
allows us to continue with the web push work flow.
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 through a push PROVIDER. It's an
authentication mechanism that allows the server to demonstrate its identity to
the push PROVIDER, 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.
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.
In the next step, BROWSER requests a data structure from SERVICE called a VAPID (Voluntary Improving Delivery Rates:
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 Using the VAPID key can help improve the overall performance of web push
server) that is sending push messages to a push service. It's an authentication notifications, specifically improving delivery rates. By streamlining the
mechanism that allows the server to demonstrate its identity to the push service, by use delivery process, the chance of delivery errors along the way is lessened.
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 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 SERVICE Web Push messages, then the BROWSER makes a subscription request to
PROVIDER which creates and stores a special URL for that BROWSER. PROVIDER which creates and stores a special URL for that BROWSER.
const applicationServerKey = urlBase64ToUint8Array('BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'); Here's a bit of code describing the above process:
// b64 is the VAPID
b64 = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';
const applicationServerKey = urlBase64ToUint8Array(b64);
const options: PushSubscriptionOptions = { const options: PushSubscriptionOptions = {
userVisibleOnly: true, userVisibleOnly: true,
applicationServerKey: applicationServerKey applicationServerKey: applicationServerKey
@ -155,68 +200,111 @@ registration.pushManager.subscribe(options)
console.error('Push subscription failed:', error); console.error('Push subscription failed:', error);
}); });
In this example, the `applicationServerKey` variable contains the VAPID public key, In this example, the `applicationServerKey` variable contains the VAPID public
which is converted to a Uint8Array using the `urlBase64ToUint8Array()` function from the key, which is converted to a `Uint8Array` using a function such as this:
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: export function toUint8Array(base64String: string, atobFn: typeof atob): Uint8Array {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
Identifying the Application Server: const rawData = atobFn(base64);
const outputArray = new Uint8Array(rawData.length);
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: for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
```
The VAPID key allows a web application to add contact information to the push messages sent to the browser push service. The options object is of type `PushSubscriptionOptions`, which includes the
This enables the push service to contact the application server in case of need or provide additional debug information about the push messages. `userVisibleOnly` and `applicationServerKey` (ie VAPID public key) properties.
Improving Delivery Rates: 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 returned data would have a form like this:
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. "endpoint": "https://some.pushservice.com/some/unique/identifier",
"expirationTime": null,
"keys": {
"p256dh": "some_base64_encoded_string",
"auth": "some_other_base64_encoded_string"
}
}
endpoint: A string representing the endpoint URL for the push service. This
URL is essentially the push service address to which the push message would
be sent for this particular subscription.
expirationTime: A DOMHighResTimeStamp (which is basically a number or null)
representing the subscription's expiration time in milliseconds since
01 January, 1970 UTC. This can be null if the subscription never expires.
The PROVIDER sends this URL back to the BROWSER. The BROWSER will then use that options: An object that contains the options used for creating the
URL to check for incoming messages by way of a special software named a "service subscription. This object itself has the following sub-properties:
worker". The BROWSER also sends this URL back to SERVICE which will use that
URL to send messages to the BROWSER via the PROVIDER. applicationServerKey: A public key your push service uses for application
server identification. This is normally a Uint8Array.
userVisibleOnly: A boolean value indicating that the push messages that
are sent should be made visible to the user through a notification.
This is often set to true.
Ultimately, the actual process of receiving messages varies from BROWSER to The BROWSER will, internally, then use that URL to check for incoming messages
BROWSER. Approaches vary from long-polling HTTP connections to WebSockets. A by way of the service worker we described earlier. 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 internal 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 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 manages the connection to the PROVIDER whilst the SERVICE must send messages
via the PROVIDER so that they reach the BROWSER. via the PROVIDER so that they reach the BROWSER service worker.
Just to remind us that in our service worker our code for receiving messages
will look something like this:
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
})
);
});
Now to address the issue of receiving notification messages on mobile devices. 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 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 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 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 permissions must be explicitly granted that allow the application to receive
notifications. Further, with an iOS device the user must enable wake on notification to push notifications. Further, with an iOS device the user must enable wake on
have their device light-up when it receives a notification (https://support.apple.com/enus/HT208081). 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 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 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 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 customized version of the Android Operating System.) There are open source
IMTERMEDIARY products such as UnifiedPush (https://unifiedpush.org/) which can 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 fulfill this role. If you are using iOS you are not permitted to make or use
custom Web Push PROVIDER. Apple will never allow anyone to do that. Apple has your own custom Web Push PROVIDER. Apple will never allow anyone to do that.
none of its own. Apple has none of its own.
It is, however, possible to have a sort of proxy working between your SERVICE and It is, however, possible to have a sort of proxy working between your SERVICE
FCM (or iOS). Services that mash up various Push notification services (like and FCM (or iOS). Services that mash up various Push notification services (like
OneSignal) can perform in the role of such proxies. OneSignal) can perform in the role of such proxies.
#4 -The INTERMEDIARY- doesn't appear to be anything we should be spending our #4 -The INTERMEDIARY- doesn't appear to be anything we should be spending our

Loading…
Cancel
Save