diff --git a/README.md b/README.md index f751f40..b0294fc 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,24 @@ # WebPush + > Web Push library for PHP [![Build Status](https://github.com/web-push-libs/web-push-php/actions/workflows/tests.yml/badge.svg)](https://github.com/web-push-libs/web-push-php/actions/workflows/tests.yml) -WebPush can be used to send notifications to endpoints which server delivers Web Push notifications as described in -the [Web Push protocol](https://datatracker.ietf.org/doc/html/rfc8030). -As it is standardized, you don't have to worry about what server type it relies on. +WebPush can be used to send push messages to endpoints as described in the [Web Push protocol](https://datatracker.ietf.org/doc/html/rfc8030). + +This push message is then received by the browser, which can then create a notification using the [service worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) and the [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API). ## Requirements PHP 8.1+ and the following extensions: - * bcmath and/or gmp (optional but better for performance) - * mbstring - * curl - * openssl (with elliptic curve support) + +- bcmath and/or gmp (optional but better for performance) +- mbstring +- curl +- openssl (with elliptic curve support) There is no support and maintenance for older PHP versions, however you are free to use the following compatible versions: + - PHP 5.6 or HHVM: `v1.x` - PHP 7.0: `v2.x` - PHP 7.1: `v3.x-v5.x` @@ -26,11 +29,21 @@ There is no support and maintenance for older PHP versions, however you are free This README is only compatible with the latest version. Each version of the library has a git tag where the corresponding README can be read. ## Installation + Use [composer](https://getcomposer.org/) to download and install the library and its dependencies. -`composer require minishlink/web-push` +```bash +composer require minishlink/web-push +``` ## Usage + +### Example + +A complete example with html+JS frontend and php backend using `web-push-php` can be found here: [Minishlink/web-push-php-example](https://github.com/Minishlink/web-push-php-example) + +### Send Push Message + ```php flush() as $report) { */ $report = $webPush->sendOneNotification( $notifications[0]['subscription'], - $notifications[0]['payload'] // optional (defaults null) + $notifications[0]['payload'], // optional (defaults null) ); ``` -### Full examples of Web Push implementations -* An example with web-push-php: [Minishlink/web-push-php-example](https://github.com/Minishlink/web-push-php-example) -* Matthew Gaunt's [Web Push Book](https://web-push-book.gauntface.com) - a must read -* Mozilla's [ServiceWorker Cookbooks](https://github.com/mdn/serviceworker-cookbook/blob/master/push-payload/README.md) (don't mind the `server.js` file: it should be replaced by your PHP server code with this library) -* Google's [introduction to push notifications](https://developers.google.com/web/fundamentals/getting-started/push-notifications/) (as of 03-20-2016, it doesn't mention notifications with payload) -* you may want to take a look at my own implementation: [sw.js](https://github.com/Minishlink/physbook/blob/9bfcc2bbf7311a5de4628eb8f3ae56b6c3e74067/web/service-worker.js) and [app.js](https://github.com/Minishlink/physbook/blob/02a0d5d7ca0d5d2cc6d308a3a9b81244c63b3f14/app/Resources/public/js/app.js) - ### Authentication (VAPID) + Browsers need to verify your identity. A standard called VAPID can authenticate you for all browsers. You'll need to create and provide a public and private key for your server. These keys must be safely stored and should not change. You can specify your authentication details when instantiating WebPush. The keys can be passed directly (recommended), or you can load a PEM file or its content: + ```php queueNotification(...); ``` In order to generate the uncompressed public and secret key, encoded in Base64, enter the following in your Linux bash: -``` + +```bash $ openssl ecparam -genkey -name prime256v1 -out private_key.pem $ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt $ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> private_key.txt ``` If you can't access a Linux bash, you can print the output of the `createVapidKeys` function: + ```php var_dump(VAPID::createVapidKeys()); // store the keys afterwards ``` On the client-side, don't forget to subscribe with the VAPID public key as the `applicationServerKey`: (`urlBase64ToUint8Array` source [here](https://github.com/Minishlink/physbook/blob/02a0d5d7ca0d5d2cc6d308a3a9b81244c63b3f14/app/Resources/public/js/app.js#L177)) + ```js serviceWorkerRegistration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(vapidPublicKey) + userVisibleOnly: true, + applicationServerKey: urlBase64ToUint8Array(vapidPublicKey) }) ``` @@ -176,6 +187,7 @@ $webPush->setReuseVAPIDHeaders(true); ``` ### Notifications and default options + Each notification can have a specific Time To Live, urgency, and topic. The WebPush standard states that `urgency` is optional but some users reports that Safari throws errors when it is not specified. This might be fixed in the future. You can change the default options with `setDefaultOptions()` or in the constructor: @@ -192,7 +204,7 @@ $defaultOptions = [ 'batchSize' => 200, // defaults to 1000 ]; -// for every notifications +// for every notification $webPush = new WebPush([], $defaultOptions); $webPush->setDefaultOptions($defaultOptions); @@ -201,25 +213,30 @@ $webPush->sendOneNotification($subscription, $payload, ['TTL' => 5000]); ``` #### TTL -Time To Live (TTL, in seconds) is how long a push message is retained by the push service (eg. Mozilla) in case the user browser -is not yet accessible (eg. is not connected). You may want to use a very long time for important notifications. The default TTL is 4 weeks. -However, if you send multiple nonessential notifications, set a TTL of 0: the push notification will be delivered only -if the user is currently connected. For other cases, you should use a minimum of one day if your users have multiple time + +Time To Live (TTL, in seconds) is how long a push message is retained by the push service (eg. Mozilla) in case the user browser +is not yet accessible (eg. is not connected). You may want to use a very long time for important notifications. The default TTL is 4 weeks. +However, if you send multiple nonessential notifications, set a TTL of 0: the push notification will be delivered only +if the user is currently connected. For other cases, you should use a minimum of one day if your users have multiple time zones, and if they don't several hours will suffice. #### urgency -Urgency can be either "very-low", "low", "normal", or "high". If the browser vendor has implemented this feature, it will save battery life on mobile devices (cf. [protocol](https://tools.ietf.org/html/draft-ietf-webpush-protocol-08#section-5.3)). + +Urgency can be either "very-low", "low", "normal", or "high". If the browser vendor has implemented this feature, it will save battery life on mobile devices (cf. [protocol](https://datatracker.ietf.org/doc/html/rfc8030#section-5.3)). #### topic -Similar to the old `collapse_key` on legacy GCM servers, this string will make the vendor show to the user only the last notification of this topic (cf. [protocol](https://tools.ietf.org/html/draft-ietf-webpush-protocol-08#section-5.4)). + +This string will make the vendor show to the user only the last notification of this topic (cf. [protocol](https://datatracker.ietf.org/doc/html/rfc8030#section-5.4)). #### batchSize + If you send tens of thousands notifications at a time, you may get memory overflows due to how endpoints are called in Guzzle. In order to fix this, WebPush sends notifications in batches. The default size is 1000. Depending on your server configuration (memory), you may want to decrease this number. Do this while instantiating WebPush or calling `setDefaultOptions`. Or, if you want to customize this for a specific flush, give it as a parameter : `$webPush->flush($batchSize)`. ### Server errors + You can see what the browser vendor's server sends back in case it encountered an error (push subscription expiration, wrong parameters...). * `sendOneNotification()` returns a [`MessageSentReport`](https://github.com/web-push-libs/web-push-php/blob/master/src/MessageSentReport.php) @@ -256,29 +273,34 @@ foreach ($webPush->flush() as $report) { **PLEASE NOTE:** You can only iterate **once** over the `\Generator` object. -Firefox errors are listed in the [autopush documentation](https://autopush.readthedocs.io/en/latest/http.html#errors). +Firefox errors are listed in the [autopush documentation](https://mozilla-services.github.io/autopush-rs/errors.html). ### Payload length, security, and performance + Payloads are encrypted by the library. The maximum payload length is theoretically 4078 bytes (or ASCII characters). -For [compatibility reasons](https://github.com/mozilla-services/autopush/issues/748) though, your payload should be less than 3052 bytes long. +For [compatibility reasons (archived)](https://github.com/mozilla-services/autopush/issues/748) though, your payload should be less than 3052 bytes long. The library pads the payload by default. This is more secure but it decreases performance for both your server and your user's device. #### Why is it more secure? + When you encrypt a string of a certain length, the resulting string will always have the same length, no matter how many times you encrypt the initial string. This can make attackers guess the content of the payload. In order to circumvent this, this library adds some null padding to the initial payload, so that all the input of the encryption process -will have the same length. This way, all the output of the encryption process will also have the same length and attackers won't be able to +will have the same length. This way, all the output of the encryption process will also have the same length and attackers won't be able to guess the content of your payload. #### Why does it decrease performance? + Encrypting more bytes takes more runtime on your server, and also slows down the user's device with decryption. Moreover, sending and receiving the packet will take more time. It's also not very friendly with users who have limited data plans. #### How can I disable or customize automatic padding? + You can customize automatic padding in order to better fit your needs. Here are some ideas of settings: + * (default) `Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH` (3052 bytes) for compatibility purposes with Firefox for Android * `Encryption::MAX_PAYLOAD_LENGTH` (4078 bytes) for maximum security * `false` for maximum performance @@ -296,10 +318,12 @@ $webPush->setAutomaticPadding(true); // enable automatic padding to default maxi ``` ### Customizing the HTTP client -WebPush uses [Guzzle](https://github.com/guzzle/guzzle). It will use the most appropriate client it finds, + +WebPush uses [Guzzle](https://github.com/guzzle/guzzle). It will use the most appropriate client it finds, and most of the time it will be `MultiCurl`, which allows to send multiple notifications in parallel. You can customize the default request options and timeout when instantiating WebPush: + ```php https://fcm.googleapis.com/fcm/send + +Web Push with VAPID will remain available at this URL. +No further action is currently required. + +### How to send data? + +The browser vendors do not allow to send data using the Push API without creating a notification. +Use some alternative APIs like WebSocket/WebTransport or Background Synchronization. ### I need to send notifications to native apps. (eg. APNS for iOS) + WebPush is for web apps. You need something like [RMSPushNotificationsBundle](https://github.com/richsage/RMSPushNotificationsBundle) (Symfony). ### This is PHP... I need Javascript! + This library was inspired by the Node.js [web-push-libs/web-push](https://github.com/web-push-libs/web-push) library. +## Reference + +### Examples, Notes and Overviews + +- Google's [introduction to push notifications](https://web.dev/explore/notifications) +- Mozilla [Push API (browser side)](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) +- Apple [Safari](https://developer.apple.com/documentation/usernotifications/sending_web_push_notifications_in_web_apps_and_browsers) +- (Archive) Matthew Gaunt's [Web Push Book](https://web-push-book.gauntface.com) +- (Archive) Mozilla's [ServiceWorker Cookbooks](https://github.com/mdn/serviceworker-cookbook/blob/master/push-payload/README.md) (don't mind the `server.js` file: it should be replaced by your PHP server code with this library) + +### Internet Engineering Task Force (IETF) + +- Generic Event Delivery Using HTTP Push [RFC8030](https://www.rfc-editor.org/rfc/rfc8030.html) +- Message Encryption for Web Push [RFC8291](https://www.rfc-editor.org/rfc/rfc8291) +- Voluntary Application Server Identification (VAPID) for Web Push [RFC8292](https://www.rfc-editor.org/rfc/rfc8292) + +### W3C + +- Working Draft [Push API](https://www.w3.org/TR/push-api/) + ## License + [MIT](https://github.com/web-push-libs/web-push-php/blob/master/LICENSE)