Web Push Generate Vapid Keys

Posted on by

Sep 04, 2019  The getVapidHeaders method expects the following input: audience: the origin of the push service. Subject: the mailto or URL for your application. PublicKey: the VAPID public key. PrivateKey: the VAPID private key. ContentEncoding: the type of content encoding to. The web-push library relies on a set of VAPID keys to work. VAPID keys are a pair of public and private keys which is used to restrict the validity of a push subscription to a specific application server, and also to identify the server that is sending the push notifications. You can generate the VAPID key pair by running the command below from the root of your project directory.

  1. Generate Vapid Keys
  2. How To Generate Vapid Keys
-->

Progressive Web Apps (PWAs) are simply web apps that are progressively enhanced with native app-like features on supporting platforms and browser engines, such as launch-from-homescreen installation, offline support, and push notifications. On Windows 10 with the Microsoft Edge (EdgeHTML) engine, PWAs enjoy the added advantage of running independently of the browser window as Universal Windows Platform apps.

We’ll need to specify the VAPID keys that will allow identifications between our app’s server and the notification server. To create a push subscription. (VAPID) spec for web push. Web developers can now use the Web Push APIs and service workers to provide an interoperable push service on the web. In order to use the Push API with VAPID I need an applicationServerKey. The PushManager subscribe method takes a VAPID key (public key alone) as a parameter and will give a subscription end point and keys to push messages. To generate VAPID keys. VAPID verification. This page helps validate or construct VAPID header data. The Headers section accepts existing header values (e.g. Ones created by a library or included as part of a subscription update). The Claims section will create a valid JSON claim, generate a VAPID key pair, and generate the proper header values.

This guide will give you an overview of PWA basics by building a simple localhost web app as a PWA using Microsoft Visual Studio and some PWA Builder utilities. The 'finished' product will work similarly across any browser that supports PWAs.

Tip

For a quick way to convert an existing site to a PWA and package it for Windows 10 and other app platforms, check out PWA Builder.

Prerequisites

You can build PWAs with any web development IDE. The following are only prerequisites for this guide, which will walk you through PWA tooling support in the Windows developer ecosystem.

  • Download the (free) Visual Studio Community 2017. You can also use the Professional, Enterprise, or Preview editions. From the Visual Studio Installer, choose the following Workloads:

    • Universal Windows Platform development
    • Node.js development

Set up a basic web app

For the sake of simplicity, we'll use the Visual Studio Node.js and Express app template to create a basic, localhost web app that serves up an index.html page. Imagine this as a placeholder for the compelling, full-featured web app you'll be developing as a PWA.

  1. Launch Visual Studio, and start a new project (File > New > Project..orCtrl+Shift+N).

  2. Under javascript, select Basic Node.js Express 4 Application. Set the name and location and click OK.

  3. Once your new project loads, Build (Ctrl+Shift+B) and Start Debugging (F5). Verify that your index.html file is loading on http://localhost:1337.

Turn your app into a PWA

Now its time to wire up the basic PWA requirements for your web app: a Web App Manifest, HTTPS and Service Workers.

Web App Manifest

A Web App Manifest is a JSON metadata file describing your app, including its name, author, entry page URL, and icon(s). Because it follows a standards-based schema, you need only supply a single web app manifest for your PWA to be installable on any platform / OS / device that supports PWAs. In the Windows ecosystem, your web app manifest signals to the Bing web indexer that your PWA is a candidate for automatic inclusion in the Microsoft Store, where it can reach nearly 700 million active monthly users as a Windows 10 app.

If this were an existing live site, you could quickly generate a web app manifest using PWA Builder. Since its still an unpublished project, we'll copy in a sample manifest.

  1. In the Visual Studio Solution Explorer, right-click the public folder and select Add > New File.., specifying manifest.json as the item name.

  2. In the manifest.json file, copy in the following boilerplate:

    If this were a real PWA, you'd obviously want to customize at least the name, start_url, short_name, and description, as well as the icons (we'll get to those next..).

    See the Web App Manifest reference on MDN web docs to learn more about the different member values and their purpose.

  3. Next, let's fill in the empty icons array with actual image paths. For that, we'll use PWA Builder's App Image Generator.

    1. Using a web browser, download this sample 512x512 PWA image.
    2. Go to the PWA Builder App Image Generator, and select the pwa.png image you just saved as the Input Image and then click the Download button.
    3. Open and Extract the zip file.
    4. In the Visual Studio Solution Explorer, right-click the public folder and Open Folder in File Explorer. Create a New folder named images.
    5. Copy all of the platform folders (android, chrome, .. windows10) from your extracted zip to the images folder and close the file explorer window. Add these folders to your Visual Studio project (in Solution Explorer, right-click images folder and select Add > Existing folder.. for each of the folders).
    6. Open (with Visual Studio or any editor) the icons.json file from the extracted zip and copy the 'icons': [..] array into your project's manifest.json file.
  4. Now we just need to associate our web app manifest with the app itself. Open the layout.pug file (in views folder) for editing, and add this line right after the stylesheet link. (Its simply Node's pug template shorthand for <link href='/manifest.json'>).

With all that in place, your web app is now serving up a manifest and homescreen-ready app icons! Try running your app (F5) and loading up the manifest:

And one of your icons:

If you publish the app live (with an actual start_url), the Bing search engine could now identify it as a candidate for automatic packaging and submission to the Microsoft Store as an installable Windows 10 app. Just make sure that your manifest includes the Quality signals for Progressive Web Apps that Bing will be scanning for:

  • name
  • description
  • At least one icon 512px square or larger (to ensure an image source of sufficient resolution for auto-generating you app's splash screen, store listing, tile image, and so on).

.. in addition to being HTTPS, using service workers, complying with Microsoft Store Policies.

HTTPS

Service Workers and other key PWA technologies that work with service workers (such as the Cache, Push, and Background Sync APIs) only work across secure connections, which means HTTPS for live sites or localhost for debugging purposes.

If you were to publish this web app as a live site (for example, by setting up an Azure free account), you'll want to ensure your server is configured for HTTPS. If you're using the Microsoft Azure App Service to host your site, it will be served over HTTPS by default.

For this guide we'll continue using http://localhost as a placeholder for a live site served over https://.

Service Workers

Service Workers is the key technology behind PWAs. They act as a proxy between your PWA and the network, enabling your website to act as an installed native app: serving up offline scenarios, responding to server push notifications, and running background tasks. Service workers also open up all kinds of new performance strategies; and website need not even be full-blown web app to take advantage of the service worker cache for fine-tuned page load performance.

Service workers are event-driven background threads that run from javascript files served up alongside the regular scripts that power your web app. Because they don't run on the main UI thread, service workers don't have DOM access, though the UI thread and a worker thread can communicate using postMessage() and onmessage event handlers.

You associate a service worker with your app by registering it to your site's URL origin (or a specified path within it). Once registered, the service worker file is then downloaded, installed, and activated on the client machine. For more, MDN web docs has a comprehensive guide on Using Service Workers and a detailed Service Worker API reference.

For this tutorial, we'll use a ready-made 'Offline page' service worker script courtesy of PWA Builder. From this, you can go on to customize it with more elaborate functionality according to your needs for performance, network bandwidth, and so on. Check out Mozilla's Service Worker Cookbook for a number of useful service worker caching 'recipe' ideas.

  1. Open https://www.pwabuilder.com/serviceworker and select the (default) Offline page service worker and click the Download service worker button.

  2. Open the download folder and copy these two files:

    • ServiceWorker1pwabuilder-sw-register.js
    • ServiceWorker1pwabuilder-sw.js

    .. to the public folder of your Visual Studio web app project. (From Visual Studio, use Ctrl+O to open file explorer to your project and navigate to the public folder).

    Its worth reviewing the code in both of these files, to get the gist of how to register a service worker that caches a designated page (offline.html) and serves it when a network fetch fails. Next, we need to create a simple 'offline.html' page as a placeholder for our app's offline functionality.

  3. In Solution Explorer, open the views/layout.pug file, and add the following line below your link tags:

    So that your site will load and run your service worker registration script.

  4. In Solution Explorer, right-click on the public folder and select Add > New File... Name it offline.html, and add a <title> and some body content, for example:

    At this point, your public folder should have three new files:

  5. In Solution Explorer, open the routesindex.js file, and add the following code just before the final command (module.exports = router;):

    This instructs your app to serve the offline.html file (when your service worker fetches it for the offline cache).

  6. Let's test out your PWA! Build (Ctrl+Shift+B) and Run (F5) your web app to launch Microsoft Edge and open your localhost page. Then,

    1. Open the Edge DevTools Console (Ctrl+Shift+J) and verify the Service worker was registered.

    2. In the Debugger panel, expand the Service Workers control and click on your origin. In the Service Worker Overview, verify your service worker is activated and running:

    3. Still in the Debugger, expand the Cache control and verify that the offline.html page has been cached.

  7. Time to try your PWA as an offline app! In Visual Studio, Stop Debugging (Shift+F5) your web app, then open Microsoft Edge (or refresh) to your website's localhost address. It should now load the offline.html page (thanks to your service worker and offline cache)!

Add push notifications

Let's make our PWA even more 'app-like' by adding client-side support for push notifications using the Push API to subscribe to a messaging service and the Notifications API to display a toast message upon receiving a message. As with Service Workers, these are standards-based APIs that work cross-browser, so you only have to write the code once for it to work everywhere PWAs are supported. On the server side, we'll use the Web-Push open-source library to handle the differences involved in delivering push messages to various browsers.

The following is adapted from the Push Rich Demo in Mozilla's Service Worker Cookbook, which is worth checking out for a number of other useful Web Push and service worker recipes.

  1. Install the NPM web-push library.

    In Visual Studio Solution Explorer, right-click your project and Open Node.js Interactive Window... In it, type:

    .. to install the Web-Push library. Then, open up your index.js file, and add the following line to the top of your file after the other requirement statements:

  2. Generate VAPID keys for your server.

    Next we'll need to generate VAPID (Voluntary Application Server Identification) keys for your server to send push messages to the PWA client. You'll only have to do this once (that is, your server only requires a single pair of VAPID keys). In the Node.js Interactive Window, type:

    The output should result in a JSON object containing a public and private key, which we'll copy into our server logic.

    In your index.js file, just before the final (module.exports = router) line, add the following:

    .. and then copy in the publicKey and privateKey values that you just generated. Feel free to customize the mailto address as well (though its not required to run this sample).

    The Mozilla Services engineering blog has a nice explainer on VAPID and WebPush if you're interested in the details of how it works behind the scenes.

  3. Handle push-related server requests.

    Now its time to set up routes for handling push-related requests from the PWA client, including serving up the VAPID public key and registering the client to receive pushes.

    In a real scenario, a push notification would likely originate from an event in your server logic. To simplify things here, we'll add a 'Push Notification' button to our PWA homepage for generating pushes from our server, and a /sendNotification server route for handling those requests.

    Still in your index.js file, append the following routes just after the VAPID initialization code you added in Step 2 above.

    With the server-side code in place, let's plumb in push notifications on the PWA client.

  4. Subscribe to push notifications.

    As part of their role as PWA network proxies, service workers handle push events and toast notification interactions. However, as it is with first setting up (or registering) a service worker, subscribing the PWA to server push notifications happens on the PWA's main UI thread and requires network connectivity. Subscribing to push notifications requires an active service worker registration, so you'll first want to check that your service worker is installed and active before trying to subscribe it to push notifications.

    Before a new push subscription is created, Microsoft Edge will check whether the user granted the PWA permission to receive notifications. If not, the user will be prompted by the browser for permission. If the permission is denied, the call to registration.pushManager.subscribe will throw a DOMException, so you'll want to handle that. For more on permission management, see Push Notifications in Microsoft Edge.

    In your pwabuilder-sw-register.js file, append this code:

    Check out the MDN documentation on PushManager and NPM docs on Web-Push for more details on how these APIs work and their various options.

  5. Set up push and notificationclick event handlers.

    With our push subscription set up, the remainder of the work happens in the service worker. First we need to set up a handler for server-sent push events, and respond with a toast notification (if permission was granted) displaying the push data payload. Next we'll add a click handler for the toast to dismiss the notification and sort through a list of currently open windows to open and/or focus the intended PWA client page.

    In your pwabuilder-sw.js file, append the following handlers:

  6. Try it out.

    Time to test push notifications in your PWA!

    1. Run (F5) your PWA in the browser. Because we modified the service worker code (pwabuilder-sw.js), we'll need to open the DevTools Debugger (F12) to the Service Worker Overview panel and Unregister the service worker and reload (F5) the page to re-register it (or you can simply click Update). In a production scenario, the browser will check regularly check for service worker updates and install them in the background. We're just forcing it here for immediate results.

      As your service worker activates and attempts to subscribe your PWA to push notifications, you'll see a permission dialog at the bottom of the page:

      Click Yes to enable toast notifications for your PWA.

    2. From the Service Worker Overview pane, try clicking the Push button. A toast notification with the (hard-coded 'Test push message from DevTools') payload should appear:

    3. Next try clicking the Send Notification button on your PWA's homepage. This time a toast with the 'payload' from our server will appear:

      If you don't click (or activate) a toast notification, it will dismiss itself after several seconds and queue up in your Windows Action Center:

      .. and with that you have the basics of PWA push notifications. In a real app, the next steps would be to implement a way to manage and store push subscriptions and to properly encrypt payload data being sent across the wire.

Going further

This guide demonstrated the basic anatomy of a Progressive Web App and Microsoft PWA development tools including Visual Studio, PWA Builder, and Edge DevTools.

Of course, there's a lot more that goes into making a great PWA beyond what we covered, including responsive design, deep-linking, cross-browser testing and other best practices (not to mention actual app functionality!), but hopefully this guide gave you a solid introduction of PWA basics and some ideas on getting started. If you have further questions on PWA development with Windows and/or Visual Studio, please leave a comment!

Check out our other PWA guides to learn how to increase customer engagement and provide a more seamless, OS-integrated app experience:

  • Windows tailoring. Using simple feature detection, you can progressively enhance your PWA for Windows 10 customers through native Windows Runtime (WinRT) APIs, such as those for customizing Windows Start menu tile notifications and taskbar jumplists, and (upon permission) working with user resources, such as photos, music and calendar.

  • PWAs in the Microsoft Store. Learn more about the benefits of app store distribution and how to submit your PWA.

Also be sure to explore the great resources from around the dev community! MDN web docs and Google Developers have excellent guides on Progressive Web Apps. https://pwa.rocks showcases real-world examples of PWAs, and https://hnpwa.com compares different frameworks and performance patterns for implementing a sample (Hacker News reader) PWA.

Introduction

The Web Push API provides the ability to deliver real time events (including data) from application servers (app servers) to their client-side counterparts (applications), without any interaction from the user. In other parts of our Push documentation we provide a general reference for the application API and a basic client usage tutorial. This document addresses the app server side portion in detail, including integrating VAPID and Push message encryption into your server effectively, and how to avoid common issues.

Update: There have been several updates since this document was originally published. Where appropriate, I’ve corrected things to reflect state as of April 25th, 2017.
Note: Much of this document presumes you’re familiar with programming as well as have done some light work in cryptography. Unfortunately, since this is new technology, there aren’t many libraries available that make sending messages painless and easy. As new libraries come out, we’ll add pointers to them, but for now, we’re going to spend time talking about how to do the encryption so that folks who need it, or want to build those libraries can understand enough to be productive.

Bear in mind that Push is not meant to replace richer messaging technologies like Google Cloud Messaging (GCM), Apple Push Notification system (APNs), or Microsoft’s Windows Notification System (WNS). Each has their benefits and costs, and it’s up to you as developers or architects to determine which system solves your particular set of problems. Push is simply a low cost, easy means to send data to your application.

Push Summary


The Push system looks like:

Application The user facing part of the program that interacts with the browser in order to request a Push Subscription, and receive Subscription Updates.
Application Server The back-end service that generates Subscription Updates for delivery across the Push Server.
Push The system responsible for delivery of events from the Application Server to the Application.
Push Server The server that handles the events and delivers them to the correct Subscriber. Each browser vendor has their own Push Server to handle subscription management. For instance, Mozilla uses autopush.
Subscription A user request for timely information about a given topic or interest, which involves the creation of an Endpoint to deliver Subscription Updates to. Sometimes also referred to as a “channel”.
Endpoint A specific URL that can be used to send a Push Message to a specific Subscriber.
Subscriber The Application that subscribes to Push in order to receive updates, or the user who instructs the Application to subscribe to Push, e.g. by clicking a “Subscribe” button.
Subscription Update An event sent to Push that results in a Push Message being received from the Push Server.
Push Message A message sent from the Application Server to the Application, via a Push Server. This message can contain a data payload.

The main parts that are important to Push from a server-side perspective are as follows we’ll cover all of these points below in detail:

  • Identifying yourself: This is important for security reasons any server that has an Endpoint can use it to send Push Messages to the corresponding application. Identifying yourself makes this more difficult.
  • Receiving subscription information: Below we’ll cover best practices for receiving subscription information on your Application Server.
  • Sending subscription updates: both without data and with encrypted data. We will also look at best practice for handling subscription updates.

Identifying Yourself


Mozilla goes to great lengths to respect privacy, but sometimes, identifying your feed can be useful.

Mozilla offers the ability for you to identify your feed content, which is done using the Voluntary Application server Identification for web Push VAPID specification. This is a set of header values you pass with every subscription update. One value is a VAPID key that validates your VAPID claim, and the other is the VAPID claim itself a set of metadata describing and defining the current subscription and where it has originated from.

VAPID is only useful between your servers and our push servers. If we notice something unusual about your feed, VAPID gives us a way to contact you so that things can go back to running smoothly. In the future, VAPID may also offer additional benefits like reports about your feeds, automated debugging help, or other features.

In short, VAPID is a bit of JSON that contains an email address to contact you, an optional URL that’s meaningful about the subscription, and a timestamp. I’ll talk about the timestamp later, but really, think of VAPID as the stuff you’d want us to have to help you figure out something went wrong.

It may be that you only send one feed, and just need a way for us to tell you if there’s a problem. It may be that you have several feeds you’re handling for customers of your own, and it’d be useful to know if maybe there’s a problem with one of them.

Generating your VAPID key

The easiest way to do this is to use an existing library for your language. VAPID is a new specification, so not all languages may have existing libraries.
Currently, we’ve collected several libraries under https://github.com/web-push-libs/vapid and are very happy to learn about more.

Fortunately, the method to generate a key is fairly easy, so you could implement your own library without too much trouble

The first requirement is an Elliptic Curve Diffie Hellman (ECDH) library capable of working with Prime 256v1 (also known as “p256” or similar) keys. For many systems, the OpenSSL package provides this feature. OpenSSL is available for many systems. You should check that your version supports ECDH and Prime 256v1. If not, you may need to download, compile and link the library yourself.

At this point you should generate a EC key for your VAPID identification. Please remember that you should NEVER reuse the VAPID key for the data encryption key you’ll need later. To generate a ECDH key using openssl, enter the following command in your Terminal:

openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem

This will create an EC private key and write it into vapid_private.pem. It is important to safeguard this private key. While you can always generate a replacement key that will work, Push (or any other service that uses VAPID) will recognize the different key as a completely different user.

Generate Vapid Keys

You’ll need to send the Public key as one of the headers . This can be extracted from the private key with the following terminal command:

Jul 25, 2018  Parallels Desktop 12 Crack With Keygen. Parallels Desktop 12.2.1 Crack is virtualization platform which allows using Windows OS on the Mac devices. It’s decent software in the virtual store. Parallels Desktop Cracked is another instrument for influencing an ideal answer for running your Window on the MAC working framework. Parallels desktop 9 mac key generator.

openssl ec -in vapid_private.pem -pubout -out vapid_public.pem

Creating your VAPID claim

VAPID uses JWT to contain a set of information (or “claims”) that describe the sender of the data. JWTs (or Javascript Web Tokens) are a pair of JSON objects, turned into base64 strings, and signed with the private ECDH key you just made. A JWT element contains three parts separated by “.”, and may look like:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiAibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIiwgImV4cCI6ICIxNDYzMDg3Njc3In0.uyVNHws2F3k5jamdpsH2RTfhI3M3OncskHnTHnmdo0hr1ZHZFn3dOnA-42YTZ-u8_KYHOOQm8tUm-1qKi39ppA

  1. The first element is a “header” describing the JWT object. This JWT header is always the same the static string {typ:'JWT',alg:'ES256'} which is URL safe base64 encoded to eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9. For VAPID, this string should always be the same value.
  2. The second element is a JSON dictionary containing a set of claims. For our example, we’ll use the following claims:

    The required claims are as follows:

    1. sub : The “Subscriber” a mailto link for the administrative contact for this feed. It’s best if this email is not a personal email address, but rather a group email so that if a person leaves an organization, is unavailable for an extended period, or otherwise can’t respond, someone else on the list can. Mozilla will only use this if we notice a problem with your feed and need to contact you.
    2. aud : The “Audience” is a JWT construct that indicates the recipient scheme and host (e.g. for an endpoint like https://updates.push.services.mozilla.com/wpush/v1/gAAAAABY.., the “aud” would be https://updates.push.services.mozilla.com. Some push services will require this field. Please consult your documentation.
    3. exp : “Expires” this is an integer that is the date and time that this VAPID header should remain valid until. It doesn’t reflect how long your VAPID signature key should be valid, just this specific update. Normally this value is fairly short, usually the current UTC time + no more than 24 hours. A long lived “VAPID” header does introduce a potential “replay” attack risk, since the VAPID headers could be reused for a different subscription update with potentially different content.

    Feel free to add additional items to your claims. This info really should be the sort of things you want to get at 3AM when your server starts acting funny. For instance, you may run many AWS S3 instances, and one might be acting up. It might be a good idea to include the AMI-ID of that instance (e.g. “aws_id”:”i-5caba953″). You might be acting as a proxy for some other customer, so adding a customer ID could be handy. Just remember that you should respect privacy and should use an ID like “abcd-12345” rather than “Mr. Johnson’s Embarrassing Bodily Function Assistance Service”. Just remember to keep the data fairly short so that there aren’t problems with intermediate services rejecting it because the headers are too big.

Once you’ve composed your claims, you need to convert them to a JSON formatted string, ideally with no padding space between elements1, for example:

{'sub':'mailto:admin@example.com','aud':'https://push.services.mozilla.com','exp':'1463087677'}

Then convert this string to a URL-safe base64-encoded string, with the padding ‘=’ removed. For example, if we were to use python:

would give us

eyJhdWQiOiJodHRwczovL3B1c2guc2VydmljZXMubW96aWxsYS5jb20iLCJzdWIiOiJtYWlsdG86YWRtaW5AZXhhbXBsZS5jb20iLCJleHAiOiIxNDYzMDAxMzQwIn0

This is the “body” of the JWT base string.

Generate

The header and the body are separated with a ‘.’ making the JWT base string.

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guc2VydmljZXMubW96aWxsYS5jb20iLCJzdWIiOiJtYWlsdG86YWRtaW5AZXhhbXBsZS5jb20iLCJleHAiOiIxNDYzMDAxMzQwIn0

The final element is the signature. This is an ECDH signature of the JWT base string created using your VAPID private key. This signature is URL safe base64 encoded, “=” padding removed, and again joined to the base string with an a ‘.’ delimiter.

Generating the signature depends on your language and library, but is done by the ecdsa algorithm using your private key. If you’re interested in how it’s done in Python or Javascript, you can look at the code in https://github.com/web-push-libs/vapid.

Since your private key will not match the one we’ve generated, the signature you see in the last part of the following example will be different.

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guc2VydmljZXMubW96aWxsYS5jb20iLCJzdWIiOiJtYWlsdG86YWRtaW5AZXhhbXBsZS5jb20iLCJleHAiOiIxNDYzMDAxMzQwIn0.y_dvPoTLBo60WwtocJmaTWaNet81_jTTJuyYt2CkxykLqop69pirSWLLRy80no9oTL8SDLXgTaYF1OrTIEkDow

Forming your headers


The VAPID claim you assembled in the previous section needs to be sent along with your Subscription Update as an Authorization header vapid token. This is a compound key consisting of the JWT token you just created (designated as t=) and the VAPID public key as its value formatted as a URL safe, base64 encoded DER formatted string of the raw key.
If you like, you can cheat here and use the content of “vapid_public.pem”. You’ll need to remove the “-----BEGIN PUBLIC KEY------” and “-----END PUBLIC KEY-----” lines, remove the newline characters, and convert all “+” to “-” and “/” to “_”.
The complete token should look like so:

Authorization: vapid t=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guc2VydmljZXMubW96aWxsYS5jb20iLCJzdWIiOiJtYWlsdG86YWRtaW5AZXhhbXBsZS5jb20iLCJleHAiOiIxNDYzMDAxMzQwIn0.y_dvPoTLBo60WwtocJmaTWaNet81_jTTJuyYt2CkxykLqop69pirSWLLRy80no9oTL8SDLXgTaYF1OrTIEkDow,k=BAS7pgV_RFQx5yAwSePfrmjvNm1sDXyMpyDSCL1IXRU32cdtopiAmSysWTCrL_aZg2GE1B_D9v7weQVXC3zDmnQ

Note: the header should not contain line breaks. Those have been added here to aid in readability

You can validate your work against the VAPID test page this will tell you if your headers are properly encoded. In addition, the VAPID repo contains libraries for JavaScript and Python to handle this process for you.

We’re happy to consider PRs to add libraries covering additional languages.

Receiving Subscription Information


Your Application will receive an endpoint and key retrieval functions that contain all the info you’ll need to successfully send a Push message. See Using the Push API for details about this. Your application should send this information, along with whatever additional information is required, securely to the Application Server as a JSON object.

Such a post back to the Application Server might look like this:

In this example, the “subscription” field contains the elements returned from a fulfilled PushSubscription. The other elements represent additional data you may wish to exchange.

How you decide to exchange this information is completely up to your organization. You are strongly advised to protect this information. If an unauthorized party gained this information, they could send messages pretending to be you. This can be made more difficult by using a “Restricted Subscription”, where your application passes along your VAPID public key as part of the subscription request. A restricted subscription can only be used if the subscription carries your VAPID information signed with the corresponding VAPID private key. (See the previous section for how to generate VAPID signatures.)

How To Generate Vapid Keys

Subscription information is subject to change and should be considered “opaque”. You should consider the data to be a “whole” value and associate it with your user. For instance, attempting to retain only a portion of the endpoint URL may lead to future problems if the endpoint URL structure changes. Key data is also subject to change. The app may receive an update that changes the endpoint URL or key data. This update will need to be reflected back to your server, and your server should use the new subscription information as soon as possible.

Sending a Subscription Update Without Data


Subscription Updates come in two varieties: data free and data bearing. We’ll look at these separately, as they have differing requirements.

Data Free Subscription Updates

Data free updates require no additional App Server processing, however your Application will have to do additional work in order to act on them. Your application will simply get a “push” message containing no further information, and it may have to connect back to your server to find out what the update is. It is useful to think of Data Free updates like a doorbell “Something wants your attention.”

To send a Data Free Subscription, you POST to the subscription endpoint. In the following example, we’ll include the VAPID header information. Values have been truncated for presentation readability.

This should result in an Application getting a “push” message, with no data associated with it.

To see how to store the Push Subscription data and send a Push Message using a simple server, see our Using the Push API article.

Data Bearing Subscription Updates


Data Bearing updates are a lot more interesting, but do require significantly more work. This is because we treat our own servers as potentially hostile and require “end-to-end” encryption of the data. The message you send across the Mozilla Push Server cannot be read. To ensure privacy, however, your application will not receive data that cannot be decoded by the User Agent.

There are libraries available for several languages at https://github.com/web-push-libs, and we’re happy to accept or link to more.

The encryption method used by Push is Elliptic Curve Diffie Hellman (ECDH) encryption, which uses a key derived from two pairs of EC keys. If you’re not familiar with encryption, or the brain twisting math that can be involved in this sort of thing, it may be best to wait for an encryption library to become available. Encryption is often complicated to understand, but it can be interesting to see how things work.

If you’re familiar with Python, you may want to just read the code for the http_ece package. If you’d rather read the original specification, that is available. While the code is not commented, it’s reasonably simple to follow.

Data encryption summary

Update: This section describes the ECE Draft-03 “aesgcm” encryption encoding.
This encoding is currently the most widely supported, however it is out-of-date.
The core principles remain the same across versions, however the final formatting differs.
There is some effort being made to have the UA report back the forms of encryption content it supports,
however that may not be available at time of publication.
You’re strongly encouraged to use a ECE library when possible.
  • Octet An 8 bit byte of data (between x00 and xFF)
  • Subscription data The subscription data to encode and deliver to the Application.
  • Endpoint the Push service endpoint URL, received as part of the Subscription data.
  • Receiver key The p256dh key received as part of the Subscription data.
  • Auth key The auth key received as part of the Subscription data.
  • Payload The data to encrypt, which can be any streamable content between 2 and 4096 octets.
  • Salt 16 octet array of random octets, unique per subscription update.
  • Sender key A new ECDH key pair, unique per subscription update.

Web Push limits the size of the data you can send to between 2 and 4096 octets. You can send larger data as multiple segments, however that can be very complicated. It’s better to keep segments smaller. Data, whatever the original content may be, is also turned into octets for processing.

Each subscription update requires two unique items a salt and a sender key. The salt is a 16 octet array of random octets. The sender key is a ECDH key pair generated for this subscription update. It’s important that neither the salt nor sender key be reused for future encrypted data payloads. This does mean that each Push message needs to be uniquely encrypted.

The receiver key is the public key from the client’s ECDH pair. It is base64, URL safe encoded and will need to be converted back into an octet array before it can be used. The auth key is a shared “nonce”, or bit of random data like the salt.

Emoji Based Diagram

Subscription DataPer Update InfoUpdate
🎯 Endpoint🔑 Private Server Key📄Payload
🔒 Receiver key (‘p256dh’)🗝 Public Server Key
💩 Auth key (‘auth’)📎 Salt
🔐 Private Sender Key
✒️ Public Server Key
🏭 Build using / derive
🔓 message encryption key
🎲 message nonce

Encryption uses a fabricated key and nonce. We’ll discuss how the actual encryption is done later, but for now, let’s just create these items.

Creating the Encryption Key and Nonce

The encryption uses HKDF (Hashed Key Derivation Function) using a SHA256 hash very heavily.

Creating the secret

The first HKDF function you’ll need will generate the common secret (🙊), which is a 32 octet value derived from a salt of the auth (💩) and run over the string “Content-Encoding: authx00”.
So, in emoji =>
🔐 = 🔑(🔒);
🙊 = HKDF(💩, “Content-Encoding: authx00”).🏭(🔐)

An example function in Python could look like so:

The encryption key and encryption nonce

The next items you’ll need to create are the encryption key and encryption nonce.

An important component of these is the context, which is:

  • A string comprised of ‘P-256’
  • Followed by a NULL (“x00”)
  • Followed by a network ordered, two octet integer of the length of the decoded receiver key
  • Followed by the decoded receiver key
  • Followed by a networked ordered, two octet integer of the length of the public half of the sender key
  • Followed by the public half of the sender key.

As an example, if we have an example, decoded, completely invalid public receiver key of ‘RECEIVER’ and a sample sender key public key example value of ‘sender’, then the context would look like:

# ⚓ (because it's the base and because there are only so many emoji)
root = 'P-256x00x00x08RECEIVERx00x06sender'

The “x00x08” is the length of the bogus “RECEIVER” key, likewise the “x00x06” is the length of the stand-in “sender” key. For real, 32 octet keys, these values will most likely be “x00x20” (32), but it’s always a good idea to measure the actual key rather than use a static value.

The context string is used as the base for two more HKDF derived values, one for the encryption key, and one for the encryption nonce. In python, these functions could look like so:

In emoji:
🔓 = HKDF(💩 , “Content-Encoding: aesgcmx00” + ⚓).🏭(🙊)
🎲 = HKDF(💩 , “Content-Encoding: noncex00” + ⚓).🏭(🙊)

Note that the encryption_key is 16 octets and the encryption_nonce is 12 octets. Also note the null (x00) character between the “Content-Encoding” string and the context.

At this point, you can start working your way through encrypting the data 📄, using your secret 🙊, encryption_key 🔓, and encryption_nonce 🎲.

Encrypting the Data

The function that does the encryption (encryptor) uses the encryption_key 🔓 to initialize the Advanced Encryption Standard (AES) function, and derives the Galois/Counter Mode (G/CM) Initialization Vector (IV) off of the encryption_nonce 🎲, plus the data chunk counter. (If you didn’t follow that, don’t worry. There’s a code snippet below that shows how to do it in Python.) For simplicity, we’ll presume your data is less than 4096 octets (4K bytes) and can fit within one chunk.
The IV takes the encryption_nonce and XOR’s the chunk counter against the final 8 octets.

The encryptor prefixes a “x00x00” to the data chunk, processes it completely, and then concatenates its encryption tag to the end of the completed chunk. The encryptor tag is a static string specific to the encryptor. See your language’s documentation for AES encryption for further information.

Sending the Data

Encrypted payloads need several headers in order to be accepted.

The Crypto-Key header is a composite field, meaning that different things can store data here. There are some rules about how things should be stored, but we can simplify and just separate each item with a semicolon “;”. In our case, we’re going to store three things, a “keyid”, “p256ecdsa” and “dh”.

“keyid” is the string “p256dh”. Normally, “keyid” is used to link keys in the Crypto-Key header with the Encryption header. It’s not strictly required, but some push servers may expect it and reject subscription updates that do not include it. The value of “keyid” isn’t important, but it must match between the headers. Again, there are complex rules about these that we’re safely ignoring, so if you want or need to do something complex, you may have to dig into the Encrypted Content Encoding specification a bit.

“p256ecdsa” is the public key used to sign the VAPID header (See Forming your Headers). If you don’t want to include the optional VAPID header, you can skip this.

The “dh” value is the public half of the sender key we used to encrypt the data. It’s the same value contained in the context string, so we’ll use the same fake, stand-in value of “sender”, which has been encoded as a base64, URL safe value. For our example, the base64 encoded version of the string ‘sender’ is ‘c2VuZGVy’

Crypto-Key: p256ecdsa=BA5v…;dh=c2VuZGVy;keyid=p256dh

The Encryption Header contains the salt value we used for encryption, which is a random 16 byte array converted into a base64, URL safe value.

Encryption: keyid=p256dh;salt=cm5kIDE2IGJ5dGUgc2FsdA

The TTL Header is the number of seconds the notification should stay in storage if the remote user agent isn’t actively connected. “0” (Zed/Zero) means that the notification is discarded immediately if the remote user agent is not connected; this is the default. This header must be specified, even if the value is “0”.

TTL: 0

Finally, the Content-Encoding Header specifies that this content is encoded to the aesgcm standard.

Content-Encoding: aesgcm

The encrypted data is set as the Body of the POST request to the endpoint contained in the subscription info. If you have requested that this be a restricted subscription and passed your VAPID public key as part of the request, you must include your VAPID information in the POST.

As an example, in python:

A successful POST will return a response of 201, however, if the User Agent cannot decrypt the message, your application will not get a “push” message. This is because the Push Server cannot decrypt the message so it has no idea if it is properly encoded. You can see if this is the case by:

  • Going to about:config in Firefox
  • Setting the dom.push.loglevel pref to debug
  • Opening the Browser Console (located under Tools > Web Developer > Browser Console menu.

When your message fails to decrypt, you’ll see a message similar to the following

You can use values displayed in the Web Push Data Encryption Page to audit the values you’re generating to see if they’re similar. You can also send messages to that test page and see if you get a proper notification pop-up, since all the key values are displayed for your use.

You can find out what errors and error responses we return, and their meanings by consulting our server documentation.

Subscription Updates


Nothing (other than entropy) lasts forever. There may come a point where, for various reasons, you will need to update your user’s subscription endpoint. There are any number of reasons for this, but your code should be prepared to handle them.

Your application’s service worker will get a onpushsubscriptionchange event. At this point, the previous endpoint for your user is now invalid and a new endpoint will need to be requested. Basically, you will need to re-invoke the method for requesting a subscription endpoint. The user should not be alerted of this, and a new endpoint will be returned to your app.

Again, how your app identifies the customer, joins the new endpoint to the customer ID, and securely transmits this change request to your server is left as an exercise for the reader. It’s worth noting that the Push server may return an error of 410 with an errno of 103 when the push subscription expires or is otherwise made invalid. (If a push subscription has expired several months ago, the server may return a different errno value.

Conclusion

Push Data Encryption can be very challenging, but worthwhile. Harder encryption means that it is more difficult for someone to impersonate you, or for your data to be read by unintended parties. Eventually, we hope that much of this pain will be buried in libraries that allow you to simply call a function, and as this specification is more widely adopted, it’s fair to expect multiple libraries to become available for every language.

See also:

  1. WebPush Libraries: A set of libraries to help encrypt and send push messages.
  2. VAPID lib for python or javascript can help you understand how to encode VAPID header data.

Footnotes:

  1. Technically, you don’t need to strip the whitespace from JWS tokens. In some cases, JWS libraries take care of that for you. If you’re not using a JWS library, it’s still a very good idea to make headers and header lines as compact as possible. Not only does it save bandwidth, but some systems will reject overly lengthy header lines. For instance, the library that autopush uses limits header line length to 16,384 bytes. Minus things like the header, signature, base64 conversion and JSON overhead, you’ve got about 10K to work with. While that seems like a lot, it’s easy to run out if you do things like add lots of extra fields to your VAPID claim set.