1
votes

so very recently I started using google's firebase cloud functions, and loved it immediately! I very quickly restructured a project I was going to work on, and included firebase in it, so I could use the cool features of firestore, in combination with cloud functions.

Up until today, everything went on smoothly; pretty much, until I decided to play with google's FCM (Firebase Cloud Messaging) to send notifications via node js. Before this, I had created some really dense functions and already deployed to my console which were working seamlessly. The tricky part is, at the time I created and deployed these functions, I initialised my firebase app in node js with admin.initalizeApp().
With this, everything worked fine(both locally & deployed) until I tried to use admin.messaging().sendToDevice...
which resulted in a very nasty error, that basically told me I couldnt send notifications if I wasnt authenticated..

The error

 (Error: 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>
>  <HEAD>
>  <TITLE>Unauthorized</TITLE>
>  </HEAD>
>  <BODY BGCOLOR="#FFFFFF" TEXT="#000000">
>  <H1>Unauthorized</H1>
>  <H2>Error 401</H2>
>  </BODY>
>  </HTML>
>  ". Status code: 401.)

Following the error, I used a few tips from some other users on stack overflow who had faced this error, and most of them suggested that I download a service key from my console, and initialise my firebase app with admin.initializeApp({credential:admin.credential.cert(serviceAccount)})

This solution worked beautifully, as it allowed me to test my notification without seeing the above error ever again.
However, when all my tests were done, and I was ready to deploy, the new function I had just created to work on notification, as well as all my previously deployed functions could not get deployed. All of a sudden, the old working functions in my console had a red exclamation mark beside them, and I had to get rid of them. Even after I cleared out all of my console and tried to redeploy all my functions, it failed, and failed and failed with no errors(context: I wasted the whole day!!! lool!) Every tip on the internet failed for me, until I reverted back to my old way of initialising my firebase app admin.initializeApp(), then booom! all my functions uploaded successfully, and then again, the authentication error appeared again when I tried to retest my notification function.....

I guess my question is: is there anything I don't know about deploying functions to the firebase console with my app initialised with a service account key I downloaded from my console? Is there something else I need to do to get my functions to deploy properly every time I init my firebase admin app with a service account key?? Because initialising the app with just .initalizeApp() works fine for all other purposes both locally and when deployed, except when using FCM. Can anyone please help with what is happening here??

2

2 Answers

1
votes

I think it can be solved by initializing two apps and using them as objects described here. One with credentials that work for other functions and one for messaging.

If you need it only for one function you can do it even inside it. I have tested it like this:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp({
  credential: admin.credential.applicationDefault()
});

exports.first_app = functions.https.onRequest(async (req, res) => {
    res.json(admin.app().name);
})

exports.other_app = functions.https.onRequest(async (req, res) => {
    var otherApp = admin.initializeApp({
    credential: **<< different credential here >>**
    }, "2nd_app");
     res.json(otherApp.name);
})
0
votes

as already mentioned, you should initialize a second app just for the new function you are creating. You should put the initialization code inside the new function like this

export const saveMap = functions.https.onRequest(async (req, response) => {
    const serviceAccount = require("./../serviceAccountKey.json");
    admin.initializeApp({
        projectId: "serviceAccount.project_id", 
        credential: admin.credential.cert(serviceAccount),
        databaseURL: "https://your_project_id_here.firebaseio.com", //update this
        storageBucket: "your_bucket_name_here.appspot.com" //update this
    }, "2nd_app")

I had the same issue and once I put the second initialization code into the new function, it worked. Note that in this code the serviceAccountKey.json is in the same folder as src and lib.