Author: Stefan Kientzler
Updated on: 2020-05-05
Posted on: 2020-05-05
Viewers: 3,744 (May 2020 until September 2020)
Last month viewers: 886 (September 2020)
Package: PHP Web Push Notifications Server
Web push notifications can solve this problem by showing notification windows with short messages telling the user there is something new on the site that they authorized to be notified when new content is publishe.
Read this tutorial article to learn about how you can implement Web push notifications in your sites using PHP.
In this article you can read about the following:
- A brief view on web push notifications
- How web push notifications works
- The subscription process
- Sending notifications
- The required key pair (VAPID)
- The subscription on the client side
- Check whether push notifications are available
- Obtain permission to deliver push notifications to the user
- Registration of the service-worker
- Implementation of the service-worker
- Receive and save subscriptions on the server
- Create and send notifications
- The header
- Encrypt the payload
- Send the notification via HTTP-request
- The subscription on the client side
- How long is a Subscription valid?
- Standardbrowser settings to display notifications
- Debugging and testing the service-workers
To implement web push notifications on your site page, appropriate functionality must be provided both on the user client side and on the server.
Subscriptions must be saved and managed on the server side and the desired messages sent. In this tutorial, the server side is implemented with PHP, since this is the most used platform and therefore this could be the easiest way to integrate into an existing system.
Packets found on the Internet all have multiple dependencies on other packets (signing, encryption, asynchronous sending of HTTP requests) and therefore also a very high overhead of unused ballast. For this reason, I decided to create a package based on the available sources that has no further external dependencies.
The complete source code of the steps described below and the PHP package for sending push notifications can be found here.
1.1. A brief view on web push notifications
Web push notifications allow messages to be sent directly to users, even if they are not currently visiting the sender's site page.
Offers, news or other relevant information can be sent at any time and users can be redirected to a desired URL. From the user's point of view, push notifications have the advantage that the entire subscription process is anonymous (the provider does not need names or contact details such as an email address or other information) and the service can be terminated at any time.
In order for a provider to be allowed to send push notifications to a user, the user must first explicitly take any action on the sender's web site to confirm that he wants to receive these messages.
In addition, a unsubscribe option is automatically integrated in each delivered push message (how and where this unsubscribe option is offered depends on the users operating system and/or browser).
The push notifications are not displayed within the web site area of a browser, but either in a separate popup window or via a service provided by the operating system (this varies depending on the device, operating system and browser and some browsers allow marginally settings - see also in the appendix).
A notification usually consists of a title, a short text (optionally with an icon and/or a picture) and usually contains a direct link to the web site it relates to.
The notification system is an extremely powerful medium for interacting with users. However, the provider should always take care not to publish too much on this medium, otherwise users will deactivate all notifications.
1.2. How web push notifications works
An external push service is connected between the browser at client side and the message provider's server. The push service is responsible for delivering messages from the server to the respective users.
When requesting information about the subscription from the web push API, the public part of a key must be passed. With the help of the private part of this key, the server in turn has to encrypt the messages before sending them.
1.2.1. The subscription process
Code for two tasks must be integrated on your site page. The first is to give the visitor the opportunity to subscribe to the web push notifications. In addition, the site page must install a service ('service-worker'), with which the client is registered on the server and received messages are processed. The service-worker is downloaded in the background to the client platform so that it can be executed outside of the context of the site page.
If the user subscribes to the service, the service-worker is registered (1). The service-worker in turn requests all required information through the web push API (2) and sends this via an HTTP request (3) to the server. The server stores this information in his database (4) so that notifications can be sent to the client.
1.2.2. Sending notifications
Notifications can now be sent from the server to all registered subscriptions with a HTTP request (2). In order to correctly identify yourself, a signature must be transmitted in the request header. This signature is generated from the public key used for registration together with the private part of the key (1). The actual message and possibly further information are transmitted as user data - also encrypted. If the formatting and encryption are correct and the signature validated, the push service sends the notification to the client (3).
1.2.3. The required key pair (VAPID)
A key pair containing a public and a private key is required for the whole process. These two keys are called VAPID keys (VAPID: Voluntary Application Server Identification for Web Push; RFC 8292 - https://tools.ietf.org/html/rfc8292) and have to be generated once for a web site.
There are various tools available for creating a VAPID key pair. Alternatively, online a key pair can be generated e.g. at https://tools.reactpwa.com/vapid. The private key should never be visible to the end user (e.g. in a JS script or somewhere else), but should only be used on the server for encryption when creating notifications. VAPID protection ensures that notifications can only be sent to clients from the authorized server.
Very detailed information about VAPID can be found in the following blog post:
After the general consideration in the previous chapter, we now turn to the actual programming.
2.1. The subscription on the client side
Some functions in connection with the web push API are processed asynchronously, which is why the promise pattern is used several times in the following code. Numerous articles on this topic can be found on the internet - at https://web.dev/promises/ beginners can find a very good explanation.
2.1.1. Check whether push notifications are available
First of all, it must be checked whether all requirements are met in order to be able to receive push notifications on the current browser. To do this, the browser must support the web push API and the Web site must run in a secure context (HTTPS).
2.1.2. Obtain permission to deliver push notifications to the user
Due to the misuse of push notifications in the past, consent to display notifications should only be requested after the user has deliberately acted on it (e.g. by clicking a button - not automatically when the page loads!).
The following function should therefore best be integrated on your own Web site using a link or button in a separate area with corresponding explanations for the push notifications. This should be seen as a rule and not just as 'best practice'.
As a provider, it should be borne in mind that many (... most) users are more likely to reject an early request without detailed explanations. And once the request has been rejected, it is difficult to get the user back on board later.
If the user has already rejected the display of notifications, he should no longer be bothered with further information regarding the subscription to the push notifications, since he must first deactivate the blocking of the notifications via the respective browser function! If necessary, this can be explained in more detail at a suitable point (e.g. under FAQ's).
The browser remembers the user's last selection. This can be determined at any time via the notification.permission property. If the user has not yet made a decision, this property is set to 'default', otherwise to 'denied' or 'granted'. With most browsers, the decision can be reset by the user in the title bar or in the page settings.
2.1.3. Registration of the service-worker
In order to receive and display push notifications, even if the user is not on that site page, a service must be registered running in the background outside the context of the website and is therefore always ready to respond to notifications.
It is not necessary to check whether the service-worker has already been registered. The browser ( the web push API) takes care of it. After registration, everything within the context of the website is done. All further steps are carried out within the service-worker.
All required functions and some helpers while testing included in PNClient.js.
2.1.4. Implementation of the service-worker
The only code in the service worker that is executed directly is to register listeners for several events:
188.8.131.52. Subscribe to push notifications and send subscription to the server
In the listener of the 'activate' event, the notification is subscribed using the web push API. The public VAPID key (see 1.2.3.) is required for this. In addition, the function requires the boolean value 'userVisibleOnly' as parameter, which must always be set to true.
Comment on this parameter
When designing the web push API, there was a consideration if this parameter can be used to control whether a message generally has to be displayed to the user or whether certain actions can only be carried out in the background.
However, there were concerns that this would create the possibility for developers to perform unwanted actions without the user's knowledge. This parameter can therefore be regarded as a 'silent agreement' that the user always get a message when a push notification arrives.
The public VAPID key must be transferred to the push manager as a UInt8 array. If successful, the push manager returns a subscription object. This object contains all information the server needs in addition to its own VAPID keys to be able to encrypt and send push notifications to this client. For this purpose, the information received must be sent to the server.
The target 'strSubscriberURL' is a service that has to be provided on your server. It accepts the transmitted data and stores it in a database. The implementation of this service is described in section 2.2.
If additional specific information is required in addition to the current subscription (e.g. login data of the user, a reference to a specific order or reservation, ...), this should also be transferred here, since this is the only direct link between client and server.
184.108.40.206. Displaying the received PUSH notifications
In contrast to the server side, on the client side the web push API (and the push services) takes over all tasks regarding verification and decryption, which enables us to concentrate on the display of the notification.
When a push notification arrives, a corresponding 'push' event is triggered to the service worker. So the first thing to do is to set up a listener to this event. All relevant information is passed to the listener in the 'event' parameter.
In addition to some internal properties, this object primarily contains the data sent by the server. The format in which the data is transmitted is the sole responsibility of the sender. At this point, pure text is assumed - this may also can be sent for test purposes from some tools or from some browsers developer tools. (See appendix).
After we have implemented the sending of the notifications in chapter 2.3. we will switch to an object encoded as a JSON string. Through this object we are able to control most of the properties of the notification to be displayed. First of all, it's just about displaying a simple text message.
To display the message the showNotification() function have to be used, which is passed the title and an option object. This option object can contain properties to control the content, format and behavior of the notification.
A more detailed description follows in Chapter 2.3 when notifications are sent from the server. The final call of waitUntil() ensures that the (asynchronously generated) notification was actually displayed before the function exits and the browser terminates the service-worker.
220.127.116.11. Respond to user actions
In order to be able to react to user actions, a listener for the 'notificationclick' event must be set up. With the 'event' parameter, this function receives all data for the notification in the 'event.notification' property. User-specific data can be passed within 'event.notification.data'. This data for example can contain an URL to be opened when the user clicks on the notification.
The function clients.openWindow() is available for opening a URL in the browser. Here, too, the waitUntil() must be used to wait for the call to end correctly before the service-worker can be terminated.
Further possible actions inside of the 'notificationclick' event are discussed in chapter 2.3 when messages are sent from the server.
2.2. Receive and save subscriptions on the server
To receive and save the subscriptions, a service is set up on the server that receives the data posted by the HTTP request from the client and stores it in a database. It must therefore first be checked whether it is a POST request. In addition, it must be checked whether the content of the request has actually been identified as JSON data.
If the request is correct, the data will be saved. For the sake of simplicity, we use a SQLite data provider here, since it creates its own data file and can be used without further configuration. By using the same data provider, the subscriptions will be accessed later to send the notifications.
To integrate the package into your own system, you can use the MySQL data provider or your own data provider that implements the PNDataProvider interface.
2.3. Create and send notifications
To send push notifications we have to follow the definitions of the web push protocol (see https://tools.ietf.org/html/draft-ietf-webpush-protocol-12). Basically, two steps are necessary when creating the push notification.
In order to identify yourself with the push service, a signature must be transferred using the VAPID key in the header of the request. The notification itself is transmitted in encrypted form and corresponding information is also passed in the header so that the browser can decrypt the received data. If the notification was decrypted correctly, the browser triggers the 'push' event to the service-worker.
2.3.1. The VAPID header
In order to identify with the push service, the server has to sign some information in JSON format with its private VAPID key and pass it in the header. The push service verifies this and, if successful, forwards the notification to the user.
The signature is given in the form of a JSON Web Token (JWT). A signed JWT is nothing more than a string, which consists of three components separated by dots:
JWTInfo . JWTData . Signature
The first two strings are JSON formatted data, which have to be 'URL safe base64' encoded, the third part contains the encrypted signature.
This contains information about the JWT itself and the encryption algorithm used.
Contains information about the sender, the recipient (not the final recipient, but the push service!) and how long the message is valid.
The signature is generated from the first two unsigned parts. To do this, they are encrypted with the ES256 algorithm (short for: ECDSA using the P-256 curve and the SHA-256 hash algorithm) using the VAPID key.
The push service now validate the JWT by decrypting the signature using the public VAPID key and comparing it with the first two parts.
The complete JWT (i.e. all three parts separated by a dot) is passed as authorization in the header. In addition, the public VAPID key 'URL safe base64' coded must be transferred in the crypto-key value.
The required VAPID headers are generated with the PNVapid class. The VAPID keys are passed once in the constructor since they do not change. The end point (i.e. the recipient) is passed on again for each notification to be generated.
2.3.2. Encrypt the payload
Since the push notifications are sent by various push service providers, the actual user data is transmitted in encrypted form. The push service is unable to decrypt and read this data.
This is defined in the 'Message Encryption for Web Push' (see https://tools.ietf.org/html/draft-ietf-webpush-encryption-09).
The techniques that are used during encryption are beyond the scope of this tutorial and are therefore not explained in detail. You will find a good explanation in the web push book by Matt Gaunt (https://web-push-book.gauntface.com) in chapter 4.2.
All required functions are provided by the PNEncryption class. This class also provides the additional request headers that are required so that the notification can be decrypted. In the constructor, this class requires the public key and the authentication code that was generated by the browser when subscribing, and of course the user data to be encrypted.
2.3.3. The payload
At this point we are now going to take a closer look at the user data that we want to send with the notification. As mentioned in section 2.1.4, the possible options that can be passed to the showNotification() function in the service-worker are explained in more detail now.
Since the format and content of the payload can be freely defined (as long as the length of the user data does not exceed approx. 4000 characters), I have decided to include all information for displaying the notification on the server side together in an object. In addition to the title and the target URL to which we want to direct the user, this object also contains the complete options for the showNotification() function.
Everything together is then JSON-encoded and sent as payload. This gives us the greatest flexibility to determine the display and behavior of the notification from PHP without having to make changes to the service worker.
18.104.22.168. The options of showNotification()
In order to address the user with a clear notification, this should consist at least of a short, meaningful title, a symbol with recognition value (-> preferably a company or product logo) and a short, precise text.
The title is passed directly as a parameter, the other two values are part of the option object. A clear recommendation regarding the format of the symbol cannot be made. In any case, a square format should be chosen, since most browsers or platforms crop other formats accordingly.
A size of 64dp (px * device pixel ratio - this gives 192px for a value of 3) has proven itself. The text should not be longer than about 200 characters. Here, too, the browsers and platforms differ in behaviour when a longer text is provided. Some limit the text to a certain number of characters, others to a certain number of lines. It should also be keep in mind here that a text that is too long usually does not receive the necessary attention from the user.
With the 'tag' option, notifications can be grouped for the user. This ensures that only the most recently received notifications with the same indicator are displayed to the user so he will not be "flooded" with a sequence of several messages of the same type. If the 'renotify' option is also set, the user will be notified, and the notifications will still be grouped in the display list. If not set, no notification will be displayed.
The support of the following properties, which can be defined to format the notification or its behaviour, varies widely between the several browsers/platforms and should therefore be used with caution.
URL to a larger image, which is usually displayed below the text. Again, it is difficult to give a rule about size or format.
URL to a (often monochrome) badge. The badge is used to better classify the sender of the message. So far, this is only supported by a few browsers - most of them display their own icon.
An action is defined by:
- action: internal ID used in 'notificationclick' event.
- title: text to be displayed.
- icon: [optional] URL to an icon assigned to the action.
The count of actions that can be displayed within a notification vary as well. An interesting article on this topic can be found at https://developers.google.com/web/updates/2016/01/notification-actions.
This allows you to set the time when the message was generated. If this option is not set, the time at which the message arrived at the user is set.
This property specifies that user interaction is required for the notification. The popup is usually displayed immediately and disappears after a certain time. If this option is activated, the popup remains until the user answers. This property should be used carefully (for very important or security issues only) as the user may find it annoying and may block the notifications permanently.
No sound is played or vibration is triggered.
A vibration pattern to run with the display of the notification. A vibration pattern must be an array with at least one member. The values are times in milliseconds where the even indices (0, 2, 4, etc.) indicate how long to vibrate and the odd indices indicate how long to pause. For example, [300, 100, 400] would vibrate 300ms, pause 100ms, then vibrate 400ms.
URL to a sound file. So far I have not found a browser that supports this.
22.214.171.124. Extend the 'push' event listener in the service-worker
To generate the notification, the PNPayload class provides all methods to define the properties described and create the Object.
Since we initially assumed pure text as user data when creating the service-worker in section 2.1.4, the event listener must now be expanded for the data contained in the notification. All that needs to be done is to decode the received JSON-formatted data and pass it on when calling the showNotification() function.
2.3.4. Send the notification via Http-request
The last step is to send the notification(s) to the respective push services via HTTP request. In order to be as independent as possible, this is done directly using cURL.
To have as little idle time as possible even with a large number of notifications to be sent, all pending messages are first generated completely, encrypted and then sent using a cURL Multirequest.
Since PHP does not support multithreading per se, this is the most elegant solution without complex external PHP extensions. After all notifications have been sent, the response codes of all requests are read in order to filter out any subscriptions that are no longer valid and, if so configured, to delete them from the database.
The complete process to send notifications
- create the VAPID header signature
- generate the notification payload
- encrypt the payload
- send via HTTP request
- If necessary, delete expired / no longer valid subscriptions
3.1. How long is a Subscription valid?
In principle, the information provided by the browser for a subscription also contains a time stamp when it expires. Almost no browser assigns a valid value to this data field (a correct value only was set by MS Edge).
According to the specification for subscriptions that have an expiration date, the push service sends a 'pushsubscriptionchange' event to the corresponding service-worker before the expiration. In this case, the service-worker should re-subscribe the notifications.
If a user quit an existing subscription (generally by blocking the notifications for the page in the browser), this is forwarded by the browser to the corresponding push service. If the server then tries to send a notification to this endpoint it is not forwarded to the browser but the push service send a response code of 410 back to the server instead.
For those cases, the Notification Server has the option of removing subscriptions that are no longer valid from its database in order to avoid unnecessary data traffic.
However, if a browser is no longer in use, uninstalled or the system is no longer used for another reason (reinstallation, outdated, defective), the existing subscriptions that do not have an expiry date are retainedin the database.
The W3C and standard browser providers are considering getting this problem under control. Unfortunately, the notification server itself doesn't have a reliable way to detect inactive subscriptions.
3.2. Standardbrowser settings to display notifications
Most (desktop) standard browsers offer the option of setting whether a service provided by the operating system or an own implementation should be used to display the notifications.
Type 'about:config' in the navigation field for the Internet URL and search for 'alerts'. The value 'alerts.useSystemBackend' controls the behaviour. If set to true, notifications are displayed by the operating system, otherwise the browsers internal implementation is used to display the notifications.
3.2.2. Google Chrome
Enter 'chrome://flags' in the navigation field for the Internet URL and then search for 'notifications'. The setting 'Enable native notifications' controls the output.
3.2.3. Microsoft Edge
Since the Edge Browser is very much integrated into the Windows operating system, it basically uses the Control Center to display the notifications.
3.3. Debugging and testing the service-workers
There are various options for debugging and testing the service-worker in the developer tools of the standard browsers.
Type 'about:debugging#/runtime/this-firefox' in the navigation field for the Internet URL. All registered service-workers are displayed below the list of the extensions.
The service-worker can be started or logged off here. Once the service worker is started, an (empty ) test message can be pushed, or the console and debugger of the service-worker can be opened.
3.3.2. Google Chrome
Open the developer tools when the page from which the notifications are subscribed is open. The registered service-worker is displayed under the menu item 'Application'. Here you can start, stop, update or unregister the service-worker. A test message can also be sent and you can switch to the source code / debugger.
3.3.3. Microsoft Edge
Similar to Chrome, MS Edge has a item in the main menu of the developer tools named 'Serviceworker'.
Web push notifications are a great means to keep users coming to a site that publishes content that they like.
You can download or install this package using PHP Composer tool by going to this download page to get the package code.
You need to be a registered user or login to post a comment
Login Immediately with your account on:
6. SaveSubscription failed with: SyntaxError: Unexpected token < in - Sagar jain (2020-10-19 05:55)
SaveSubscription failed with: SyntaxError: Unexpected token < in... - 1 reply
Read the whole comment and replies