3

I'm trying to set up a GCP Cloud Function to generate the email verification link using admin.auth().generateEmailVerificationLink, but it throws the error:

Error: Credential implementation provided to initializeApp() via the "credential" property has insufficient permission to access the requested resource. See https://firebase.google.com/docs/admin/setup for details on how to authenticate this SDK with appropriate permissions.

I was able to reproduce this error with the following Cloud Function code:

index.js:

const admin = require('firebase-admin');

admin.initializeApp();

exports.helloWorld = (req, res) => {
  execute(res);
};

const execute = async (res) => {
  const email = 'test@test.com';
  const url = 'https://example.firebaseapp.com';
  const link = await admin.auth().generateEmailVerificationLink(email, { url });
  console.log(link);
  res.status(200).send(link);
};

package.json:

{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
    "firebase-admin": "^10.0.2"
  }
}

My Firebase Admin Service Account (firebase-adminsdk-XXX@example.iam.gserviceaccount.com) has the roles:

  • Firebase Admin SDK Administrator Service Agent
  • Service Account Token Creator

I also viewed the API Key in Firebase Console, found it in GCP (Browser key (auto created by Firebase), and see that it has the following APIs selected:

  • Cloud Firestore API
  • Cloud Functions API
  • Firebase Installations API
  • Token Service API
  • Identity Toolkit API

I tried following the provided link (https://firebase.google.com/docs/admin/setup), but it seems specific to setting up admin outside of a GCP Cloud Function (see https://firebase.google.com/docs/admin/setup#initialize-without-parameters). I also read through https://firebase.google.com/docs/auth/admin/email-action-links, but there were no helpful details that I could find.

I tried using functions.https.onCall instead of the regular GCP exports.

I tried setting FIREBASE_CONFIG={"projectId":"example","storageBucket":"example.appspot.com","locationId":"<my-region>"} and GCLOUD_PROJECT=example as runtime env vars.

Ryan Saunders
  • 359
  • 1
  • 13
  • What happens when you initialize with the ADC? ```initializeApp({ credential: applicationDefault(), databaseURL: 'https://.firebaseio.com' });``` Also, are you using cloud functions or firebase functions? – Alexander N. Nov 09 '22 at 17:30
  • @AlexanderN. good question! No, it didn't change anything. Cloud Functions === Firebase Functions (https://stackoverflow.com/a/42859932/6090140), but if you mean is it using the `firebase-functions` `functions.https.onRequest` method or whether it's using the GCP default, I tried both and no difference. – Ryan Saunders Nov 09 '22 at 17:49
  • 1
    My assumption is that it might be a problem with the service account that is running your cloud function lacking some permission for firebase. Are you able to inspect your cloud functions service account and validate this? Can you also validate that you are running in the same project as your firebase project? – Alexander N. Nov 09 '22 at 18:00
  • @AlexanderN. so I went to the Firebase console > Project settings > Service Accounts > Firebase Admin SDK to view the service account name (`firebase-adminsdk-XXX@my-project.iam.gserviceaccount.com`). I then went to GCP IAM and saw that it has the roles of "Firebase Admin SDK Administrator Service Agent" and "Service Account Token Creator". Does it need any other roles? Yes, same project. I can view the Cloud Function in both GCP and in the Firebase Console. – Ryan Saunders Nov 09 '22 at 18:27
  • @AlexanderN. I also added details regarding the Web API Key in my original question, in case that's relevant. – Ryan Saunders Nov 09 '22 at 18:52
  • Hey, I believe I see what the issue is and it comes down to a runtime environment variable. I added my notes below. – Alexander N. Nov 09 '22 at 19:39

2 Answers2

4

The issue was that because I was deploying the function on GCP (and not through Firebase), the actual service account that runs the function is not the one specified in the Firebase console (firebase-adminsdk-XXX@example.iam.gserviceaccount.com), it is instead the App Engine default service account:

At runtime, Cloud Functions defaults to using the App Engine default service account (PROJECT_ID@appspot.gserviceaccount.com)

Source: https://cloud.google.com/functions/docs/concepts/iam#access_control_for_service_accounts

So in this case, the call worked once I gave my account example@appspot.gserviceaccount.com the roles:

  • Firebase Admin SDK Administrator Service Agent
  • Service Account Token Creator

As a side note, no additional options were needed for admin.initializeApp() nor were the Runtime Environment Variables needed.

Ryan Saunders
  • 359
  • 1
  • 13
  • 1
    Ahhh okay. I used the default compute engine service account for my test – Alexander N. Nov 09 '22 at 23:22
  • @Ryan Saunders, is it safe to add the Firebase Admin SDK Administrator Service Agent ? – Courvoisier Jan 14 '23 at 13:07
  • @Courvoisier "safe" is relative. Any service account being given permissions extends its use. In this case though, it works for us because the only way we operate on our app engine service account is through codified verifiable changes. If your service account can be accessed and operated by other users, then that's a possible attack vector. That's just one example of how to think about. There is no such thing as safe vs not safe, just how things are implemented and the level of risk you're taking on. – Ryan Saunders Jan 16 '23 at 17:40
0

Since you did not deploy using the Firebase CLI and instead deployed with the Google Cloud Console, the FIREBASE_CONFIG environment variable was not deployed with it. See note. What you need to do is add a Runtime Variable of something like this:

Key: FIREBASE_CONFIG Value: {"projectId":"myawesomeproject","storageBucket":"myawesomeproject.appspot.com","locationId":"us-central"}

Key: GCLOUD_PROJECT value: myawesomeproject

I was able to test this using the Firebase CLI to deploy a function known to work and then compared the function variables and environment settings against a function deployed from the Google Cloud Console.

Alexander N.
  • 1,458
  • 14
  • 25
  • thanks for digging into it, but it didn't work. I tried it with `admin.initializeApp()`, `admin.initializeApp({ credential: applicationDefault(), databaseURL: ...})`, and even `admin.initializeApp({ credential: applicationDefault(), databaseURL: ..., projectId: ..., storageBucket: ..., locationId: ...})`. I also tried using `functions.https.onCall` instead of the regular GCP exports. No change in the error message though. – Ryan Saunders Nov 09 '22 at 21:14