Seph Reed
Seph Reed

Reputation: 11028

WebPush.sendNotification Node.js giving 401 "header must be specified" error on googleapis endpoint

I'm getting the following error:

WebPushError: Received unexpected response code
    at IncomingMessage.<anonymous> (/Users/sepp/.../node_modules/web-push/src/web-push-lib.js:347:20)
    at IncomingMessage.emit (node:events:406:35)
    at endReadableNT (node:internal/streams/readable:1331:12)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  statusCode: 401,
  headers: {
    'content-type': 'text/plain; charset=utf-8',
    'x-content-type-options': 'nosniff',
    'x-frame-options': 'SAMEORIGIN',
    'x-xss-protection': '0',
    date: 'Wed, 01 Feb 2023 19:57:43 GMT',
    'content-length': '40',
    'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000',
    connection: 'close'
  },
  body: 'authorization header must be specified.\n',
  endpoint: 'https://fcm.googleapis.com/fcm/send/duj-etc-etc

The code involved is:

import * as webPush from "web-push";

const subDetails = {
  endpoint: "https://fcm.googleapis.com/fcm/send/duja6etc-etc",
  expirationTime: null,
  keys: {
      p256dh: "BHtwM-etc-etc",
      auth: "aYkx0etc-etc"
  }
}

await webPush.sendNotification(subDetails, "test message", );

I found this issue on Github, and there was some debilitation as to whether or not it has to do with the environment. I am running my front-end page and back-end server both locally. There is a 'x-frame-options': 'SAMEORIGIN' header in the response.

As you can see from the code above, I do not have VAPID set up.

If I use console.log(webPush.generateRequestDetails(pushSub.details, args.msg)) to see what the headers and body of the request are, I get the following details, which show that the auth header is not set:

{
  method: 'POST',
  headers: {
    TTL: 2419200,
    'Content-Length': 121,
    'Content-Type': 'application/octet-stream',
    'Content-Encoding': 'aes128gcm'
  },
  body: <Buffer ....>,
  endpoint: 'https://fcm.googleapis.com/fcm/send/duj-etc-etc'
}

Questions

  1. Are there any special requirements for localhost stuff?
  2. What does it take for auth headers to be included?

EDIT: The browser I'm using is Opera GX. I did find a browser support table, which says that opera does not yet support push on desktop. The error still seems to imply something else may be the issue. Testing in Firefox Dev Edition, it works! Unfortunately, in Chrome the same exact error as Opera GX is given.

Upvotes: 1

Views: 1555

Answers (1)

Seph Reed
Seph Reed

Reputation: 11028

The issue is two-fold.

Issue #1: Opera GX does not support push notifications on desktop. Check this table for details on your browser.


Issue #2: For any push services which use a https://fcm.googleapis.com/fcm/send/ endpoint, you'll need auth headers. To create them, you'll need a VAPID. Here's how to set that up in web-push:

  1. Create your public and private keys in command line (you many need to do ./node_modules/.bin/web-push instead): $ web-push generate-vapid-keys --json
  2. Store the private key somewhere safe only your server can get to it. Public key will be needed by both front and back end.
  3. Update your code to generate auth headers and add them to the request
    import * as webPush from "web-push";
    
    const subDetails = {
      endpoint: "https://fcm.googleapis.com/fcm/send/duja6etc-etc",
      expirationTime: null,
      keys: {
        p256dh: "BHtwM-etc-etc",
        auth: "aYkx0etc-etc"
      }
    }
    const VAPID = {
      publicKey: "lalalla-etc-etc-put-anywhere",
      privateKey: "lCRVkwS-etc-etc-put-somewhere-safe"
    }
    
    const parsedUrl = new URL(subDetails.endpoint);
    const audience = `${parsedUrl.protocol}//${parsedUrl.hostname}`;
    
    // technically, the audience doesn't change between calls, so this can be cached in a non-minimal example
    const vapidHeaders = webPush.getVapidHeaders(
      audience,
      'mailto: [email protected]',
      VAPID.publicKey,
      VAPID.privateKey,
      'aes128gcm'
    );
    
    await webPush.sendNotification(subDetails, "test msg", {
      headers: vapidHeaders
    });
    
  4. The code above should work fine in chrome and firefox. Let me know if this minimal example needs more for some other browser.

Upvotes: 1

Related Questions