With this day I finally implemented a simple subscription system using push notifications for new posts! 🚀
To get notified when a new post is published, simply check the box in the navigation panel and you're registered. You will be asked to give your permission for this site to send you notifications. You can confirm it, I won't spam you, I promise!
If there's no button visible, your browser lacks support for the required technologies 👎
If you're interested in how this system works you can continue reading the following rough explanation.
These systems/technologies are required to run a system like this:
- HTTPS: This is needed to run Service Workers because of security reasons, since someone could "man-in-the-middle" you and take power of its features.
I would like to see SW work on HTTP too, but wanting it isn't enough, we need to find a way to overcome the security issues. Eg, with ServiceWorker right now, if you connect to my café's wifi network and visit any HTTP page, I can MITM you and throw in iframes/redirects to make you request any other urls. If those urls are also HTTP, I can hijack those and install serviceworkers. I now own those origins, even once you're back on "safe" wifi.
HTTPS prevents all that.
Service Worker: You can think of it like a shared webworker sitting between the document and network, thus enabling features like offline usage of website and background actions even if the site is not opened. You can find some example use cases in the 'Service Worker Cookbook' by Mozilla.
Push API: This API makes it possible to send push messages to a Service Worker running on a site. The Service Worker then can take actions like making the message visible to the user in any way.
Google Cloud Messaging (GCM): A free service to send messages from servers to clients
Server + Storage: A simple server handling subscribe/unsubscribe requests and persist subscriptions in a storage, e.g. a database. In this case it's a little node.js application which I might open-source if it reaches a stable status.
When visiting this blog your browser gets checked for Service Worker support and a new Instance gets registered and installed if the check succeeds. Within this step the subscription button in the navigation panel is created, since working with push subscriptions requires a ready service worker.
When you hit the button to subscribe for this blog, you get asked for a notification permission. As soon as it's given, a push subscription needs to be created. Such subscription instance basically consists of an endpoint URI and a method to retrieve two kinds of encryption keys. The URI, fittingly, identifies the users' push subscription along with an endpoint and can be used to send messages to the according Service Worker instance. The keys are needed if you want to send payloads within your messages (which is very likely).
Both of the keys and the subscription endpoint URI are sent to the server in the next step. It validates the request and persists the URI along with the keys in a database. If this process is done, a successful response is returned to the client, so the state of the subscription button can be set to active.
From this point the client is ready to receive push notifications. To do this, the servers iterates the subscription list and send a notification to the various endpoint URIs. If a payload is provided, the keys from the storage are used to encrypt the payload.
The use case for this blog is to notify users about new posts, so the payload simply consists of the appropriate URL.
For Node.js environments I can recommend the web-push module by marco-c. It handles all the encryption stuff for you and simplifies sending the push messages as well.
When the Service Worker finally gets the notification, a special event is fired, from which you can extract the payload and other data. In this case a notification is created with the data from the payload with a 'click' listener attached to it. This listener dispatches an event if the user opens the notification. Since the goal is to notify about new posts, a new tab is opened with the URL from the payload pointing to the post.
A more detailed workflow summary is explained on Mozilla's MDN.
The first thing to note is that Service Workers are currently not supported by Edge and Safari. But if you take a look at Jake Archibald's "Is ServiceWorker ready"-table you'll see that Microsoft prioritized this technology highly and Webkit has shown interest.
Another aspect which you should consider is that Chrome and Firefox need to be running in order to receive push messages.
In addition, Chrome < 50 does not support encryption keys for a subscription, so you cannot send payloads within push messages since they need to be encrypted. Luckily, this problem is fixed in later versions. To work around this issue for now, you could still listen for the notification event and then send an extra request to the server to retrieve the actual data.
For this blog I chose not doing this extra request so the user is just redirected to this landingpage, where the newest posts are listed.
This is my first experience with Service Workers and although I only used a small fraction of its possibilities, I really enjoyed it. They are very powerful and bring features to the web which seemed to be available for native apps only. And with today's browser like Chrome Canary or Firefox Developer Edition, the debugging is not as complex as I thought (though that may only apply to small projects like this).
I hope that Safari/Apple will finally engage more in implementing Service Workers for webkit, so this technology can reach much more users and gain the attention it deserves.
Here are some resources which helped me a lot building this system.