Web Push (VAPID)

RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push (RFC 8030). Operators sign JWTs with an ECDSA-P256 key to identify themselves to the push service; the browser-side subscription includes the operator's VAPID public key in applicationServerKey. RFC 8292 §3 mandates ES256; the framework uses node:crypto for ECDSA because the protocol is not PQC-yet (browser push services don't accept ML-DSA today; track draft-ietf-webpush-vapid-pqc for the migration).

b.webPush.buildVapidAuthHeader({ subscription, contact, privateKeyPem, publicKeyPem }) returns the Authorization: vapid t=, k= header value the operator sets on the push-request POST to the push-service endpoint.

b.webPush.generateVapidKeypair() returns { publicKeyPem, privateKeyPem, publicKeyB64Url } — the b64url-encoded public key is what the browser code passes as applicationServerKey.

b.webPush.generateVapidKeypair() #

stable0.10.16

Generate a fresh ECDSA-P256 keypair suitable for VAPID. Returns { publicKeyPem, privateKeyPem, publicKeyB64Url }. The b64url- encoded public key is what the browser code passes as applicationServerKey to pushManager.subscribe.

var kp = b.webPush.generateVapidKeypair();
// Browser:
//   pushManager.subscribe({ applicationServerKey: kp.publicKeyB64Url })

b.webPush.buildVapidAuthHeader(opts) #

stable0.10.16
{
  subscription:    { endpoint: string },              // browser-returned subscription
  contact:         string,                            // mailto:... or https:... per RFC 8292 §2
  privateKeyPem:   string,                            // ECDSA-P256 PEM-encoded private key
  publicKeyB64Url: string,                            // public key from generateVapidKeypair()
  ttlSec:          number,                            // optional, default 12h
}

Build the Authorization: vapid t=, k= header value per RFC 8292 §3. The JWT claims (aud / exp / sub) are computed from the subscription endpoint origin + operator contact; exp defaults to 12 hours (RFC 8292 §2 caps at 24 hours).

var hdr = b.webPush.buildVapidAuthHeader({
  subscription: { endpoint: "https://fcm.googleapis.com/wp/abc" },
  contact:      "mailto:ops@example.com",
  privateKeyPem:   kp.privateKeyPem,
  publicKeyB64Url: kp.publicKeyB64Url,
});
// → "vapid t=, k="

b.webPush.encrypt(opts) #

stable0.10.16
{
  subscription: { endpoint, keys: { p256dh, auth } },
  payload:      Buffer|string,
  ttlSec:       number,                       // default 28d (RFC 8030 §5.2)
}

Encrypt a Web Push message payload per RFC 8291 (Message Encryption for Web Push) using the aes128gcm content-coding per RFC 8188. Returns { body, headers }: - body is the Buffer to POST to the subscription endpoint - headers carries the spec-required Content-Encoding + Content-Length + TTL (caller-overridable) so operators wire them onto the push-request alongside the VAPID Authorization.

The recipient's subscription object provides p256dh (their ECDH P-256 public key, base64url) and auth (16-byte auth secret, base64url). The framework computes the ephemeral keypair, performs ECDH, runs the two-stage HKDF per RFC 8291 §3.4, and AES-128-GCM encrypts with the padded plaintext per RFC 8188 §2.

var e = b.webPush.encrypt({
  subscription: { endpoint: sub.endpoint, keys: { p256dh, auth } },
  payload: "hello",
});
b.httpClient.request({
  url: sub.endpoint, method: "POST",
  headers: Object.assign({}, e.headers, {
    Authorization: vapidHeader,
  }),
  body: e.body,
});

Last updated 2026-08-08T16:39:49.652Z by seeder.