firebase / firebase-admin-node

Firebase Admin Node.js SDK
https://firebase.google.com/docs/admin/setup
Apache License 2.0
1.62k stars 366 forks source link

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

Open NolanOfficial opened 1 month ago

NolanOfficial commented 1 month ago

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

[REQUIRED] Step 2: Describe your environment

[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);
}
google-oss-bot commented 1 month ago

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

lahirumaramba commented 1 month ago

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

NolanOfficial commented 1 month ago

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.

lahirumaramba commented 1 month ago

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?

NolanOfficial commented 1 month ago

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.