20

I am running a node server with the firebase admin sdk. However, everytime I try to send a push notification from the server, I get a 401 error.

Here's the exact error I'm getting:

  errorInfo: {
code: 'messaging/authentication-error',
message: 'An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "<HTML>\n' +
  '<HEAD>\n' +
  '<TITLE>PROJECT_NOT_PERMITTED</TITLE>\n' +
  '</HEAD>\n' +
  '<BODY BGCOLOR="#FFFFFF" TEXT="#000000">\n' +
  '<H1>PROJECT_NOT_PERMITTED</H1>\n' +
  '<H2>Error 401</H2>\n' +
  '</BODY>\n' +
  '</HTML>\n' +
  '". Status code: 401.'
},
codePrefix: 'messaging'

I'm not exactly sure why I don't have permissions to the project. I have setup my service account, and downloaded the .json file. I even went into the gcloud platform and tried to add any permission that looked correct. Below are all the permissions associated with my service account:

permissions

I am running the server locally, and initialized the app like this:

const admin = require('firebase-admin');
const messaging = require('firebase-admin/messaging');
const serviceAccount = require('<path-to-key>');

const fbApp = admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    projectId: '<PROJECT_ID>',
    databaseURL: '<DB_URL>'
});

I am not sure what else to do as I've looked through the v1 documentation multiple times and still don't have any clue as to what permissions I'm lacking. I even made sure to "firebase login" into the correct google account to see if that could've been an issue.

Here's my code to send a message:

const sendPushNotifications2 = async (topic, reminder) => {
    const payload = genPayload2(reminder);
    //await messaging.getMessaging(fbApp).sendToTopic(topic, payload);
    await admin.messaging(fbApp).sendToTopic(topic, payload);
};

I have verified the client_id, client_email, and private_key_id values in the .json file. I haven't yet verified the private_key property because I'm not sure where to find it.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
Tlal
  • 591
  • 1
  • 6
  • 14

3 Answers3

42

The problem is that new Firebase projects have only the new "Firebase Cloud Messaging API (V1)" enabled by default, and with that configuration - the official firebase-admin NodeJS library - that uses the new V1 API - will not be able to send messages and will get 401 PROJECT_NOT_PERMITTED errors.

To be able to send message from your server, you MUST also enable the older "legacy" API.

  1. From your Firebase console, go to Project Settings and open the Cloud Messaging tab.
  2. Make sure that both APIs listed are enabled - see screenshot of how it should look: enter image description here
  3. If any API is not enabled, for the API that is disabled click the three dots menu on the right and select "Manage API"
  4. In the Google Cloud Console page that opens, click the "Enable" button.

Note:

  • This is basically the same answer as @Tial's self answer, but I was confused by it and (as commented) it got some things wrong - it took me a while to understand it (and the linked answer) to get it right, so I had to clarify.
  • As of this writing this issue impacts all uses of the Cloud Messaging APIs, regardless if you use the new (V1) or the lagacy API. I expect Google to fix this at some point.
Guss
  • 30,470
  • 17
  • 104
  • 128
  • Is this still the case, even with the legacy version getting phased out? – Michael Plischke Jul 17 '23 at 07:27
  • 1
    I have not checked recently, but as of the beginning of this year (when the legacy API has been notably deprecated in the UI and disabled by default - for a couple of years now) - this was still the case. – Guss Jul 17 '23 at 07:29
  • In that case, is there an easy way to confirm that I'm using v1 functions with a service account and not legacy functions with an API key? – Michael Plischke Jul 17 '23 at 07:38
  • @MichaelPlischke - yes, it is easy: do not provide the API key to the applications. – Guss Jul 17 '23 at 12:12
  • You don't need the deprecated legacy API for topic messaging. Just use the same `getMessaging().sendMessage(message)`-Function as for other messages and add a `topic` key to the message as described in https://stackoverflow.com/a/76580839/2417204 – n2o Aug 01 '23 at 14:21
33

It turns out that I had this API disabled: "Firebase In-App Messaging API" It also turns out that this question was asked before, but I wasn't able to find it. Here's the answer

If anyone else runs into this issue this was my fix:

  1. go to the google cloud platform website
  2. go to APIs and Services
  3. go to Enabled APIs & Services
  4. click + Enable APIs and Services
  5. search for Firebase In-App Messaging API and make sure it's enabled.

You can also just search for messaging and make sure that all the cloud messaging APIs are enabled.

Zach Jensz
  • 3,650
  • 5
  • 15
  • 30
Tlal
  • 591
  • 1
  • 6
  • 14
1

I had the same issue and since Cloud Messaging API (Legacy) is now deprecated I tried hard to get Firebase Cloud Messaging API (V1) to work.

I got lucky with using getMessaging(app).send() method instead .sendToTopic() or .sendToDevice() because those are using legacy api. More here: https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.messaging.md#messaging_class

So in your case, this should work:

import admin from 'firebase-admin';
import { initializeApp } from 'firebase-admin/app'; // I recommend you use initializeApp instead because admin is legacy
import { getMessaging } from 'firebase-admin/messaging';
import serviceAccount from '<path-to-key>';

const fbApp = initializeApp({
    credential: admin.credential.cert(serviceAccount),
    projectId: '<PROJECT_ID>',
    databaseURL: '<DB_URL>'
});

const messaging = getMessaging(fbApp);

const sendPushNotifications2 = async (topic, reminder) => {
    // message is defined as [BaseMessage][1] extended by [topic][2] or [token][3] or [condition][4]
    const message = {
        ...reminder, // suppose reminder is BaseMessage
        topic: topic
    };
    
    await messaging.send(message);
};
svrznjak
  • 11
  • 2
  • Please visitors, check out this answer and don't rely on old / deprecated APIs :D this is the way to go: Same message structure, just add a `topic` field to the message and you're done! – n2o Aug 01 '23 at 14:19