firebase-admin-node icon indicating copy to clipboard operation
firebase-admin-node copied to clipboard

[Messaging] Unable to send APN (Apple Push Notifications) with Firebase Messaging and Cloud Functions.

Open NolanOfficial opened this issue 1 year ago • 5 comments

[READ] Step 1: Are you in the right place?

  • For issues related to the code in this repository file a Github issue.
  • If the issue pertains to Cloud Firestore, read the instructions in the "Firestore issue" template.
  • For general technical questions, post a question on StackOverflow with the firebase tag.
  • For general Firebase discussion, use the firebase-talk google group.
  • For help troubleshooting your application that does not fall under one of the above categories, reach out to the personalized Firebase support channel.

[REQUIRED] Step 2: Describe your environment

  • Operating System version: iOS 17.4+
  • Firebase SDK version: v12.3.0
  • Firebase Product: Cloud Functions | Firebase Messaging
  • Node.js version: 20.16.0
  • NPM version: 10.8.2

[REQUIRED] Step 3: Describe the problem

When trying to send an iOS push notification (APN) through firebase cloud functions using firebase messaging, it fails with an error message (summarized from cloud functions log):

cause: HTTPParserError: Response does not match the HTTP/1.1 protocol (Expected HTTP/)
code: 'HPE_INVALID_CONSTANT',
data: '\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x01\x00\x00\x10\x00\x00\x03\x00\x00\x00\x01\x00\x05\...'

Sending an APN request via cURL through the terminal, using http/2 gives a valid response and the notification shows. It also works via Firebase Messaging dashboard when testing a push notification.

All APN certificates (the method I'm using) have been added to Firebase in project settings, and they have all been verified.

Steps to reproduce:

  1. Create a cloud function to send a push notification.
  2. Send the notification to an IOS device.

Relevant Code:

Index.ts file

import {onDocumentUpdated} from "firebase-functions/v2/firestore";
import {initializeApp} from "firebase-admin/app";

initializeApp();

const path = "";
exports.change = onDocumentUpdated(path, async (event) => {

  if (!event.data) {
    error("No data associated with the event");
    return;
  }

  debug("", {
    docRef: event.data.after.ref,
    exists: event.data.after.exists,
    createdTime: event.data.after.createTime,
  });

// Would get data user tokens here from firestore, irrelevant code"

const promises = [];
const promise = await sendiOSNotification(deviceToken, fcmToken, "Title Test", "Body Test");
promises.push(promise);
return await Promise.all(promises)
});

APN notification file.ts

import {getMessaging} from "firebase-admin/messaging";

/**
* A simple function to deliver iOS push notifications
* @param {string} deviceToken the user's device token
* @param {string} fcmToken the user's firebase fcm token
* @param {string} alertTitle the notification title
* @param {string} alertBody the notification body
*/
export async function sendiOSNotification(
  deviceToken: string,
  fcmToken: string,
  alertTitle: string,
  alertBody: string) {
  const payload = {
    token: fcmToken,
    notification: {
      title: alertTitle,
      body: alertBody,
    },
  };
  return await getMessaging().send(payload);
}

NolanOfficial avatar Aug 04 '24 10:08 NolanOfficial

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

google-oss-bot avatar Aug 04 '24 10:08 google-oss-bot

Thanks for reporting this. Are you able to reproduce this issue on your local development environment or does this only happen on Cloud Functions?

lahirumaramba avatar Aug 06 '24 16:08 lahirumaramba

So I'm using Node.js and tried to replicate the function on a custom node from scratch (making a request to Apple's APN server). Using http2 gave me the same error warning. Perhaps it's JavaScript's http2 library that's causing the issue but I'm not a JS expert so maybe there was something I missed.

NolanOfficial avatar Aug 07 '24 21:08 NolanOfficial

The code sample you provided shows that you are using the send() API. Are you sending a single message? If so, any reason you would prefer to use http/2 for that?

lahirumaramba avatar Aug 07 '24 22:08 lahirumaramba

I'm sending a single notification, yes. And the reason for http2 is the apple requirement for sending and receiving APNs (as they will only accept requests that way). I noticed Firebase Messaging Nodejs uses http/2 by default and has an option for enabling legacy requests. I tried both, but both failed.

NolanOfficial avatar Aug 09 '24 09:08 NolanOfficial

Tested this on the latest build and is now working.

NolanOfficial avatar Oct 24 '24 20:10 NolanOfficial