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

[FR] Permission 'iam.serviceAccounts.signBlob' denied on resource, when initializing an App with Service Account ID from another Firebase project

Open ruslan-simonenko opened this issue 1 year ago • 9 comments

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

Yes

[REQUIRED] Step 2: Describe your environment

  • Operating System version: Google Cloud environment
  • Firebase SDK version: 11.11.1
  • Firebase Product: auth
  • Node.js version: 18
  • NPM version: 10.2.3

[REQUIRED] Step 3: Describe the problem

The documentation for custom token creation implies that one can init an app using service account ID, and create a token with it: https://firebase.google.com/docs/auth/admin/create-custom-tokens#using_a_service_account_id

This does not seem to be the case for multi-project setup. The troubleshoot guide is unfortunately unhelpful. And error message that Auth module provides is counter-productive as it says that service account has a permission issue, which it does not.

This problem was previously mentioned in #1410.

Steps to reproduce:

Goal: create a token for Project A from Project B's function.

  1. Create two Firebase Projects A & B.
  2. Create service account "Token Creator" in Project A.
  3. Grant it "Service Account Token Creator" permission.
  4. Create Firebase callable Function (v2) that creates JWT token:
  5. Call this function from Project B.

Expected: token is created Actual:

Unhandled error FirebaseAuthError: Permission 'iam.serviceAccounts.signBlob' denied on resource (or it may not exist).; Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on how to use and troubleshoot this feature.

This error is hard to debug, as one would assume that something is wrong with the way Service Account is set up. However the reality is that the issue lies with how the App is initialized - it must be initialized with Service Account Private Key, not ID.

It would have saved me a few hours if the library threw an error during App initialization: "This service account must be initialized with a private key, as it belongs to a different Firebase project", or if at least the troubleshoot guide described this caveat.

Relevant Code:

import { onCall } from 'firebase-functions/v2/https';
import { auth, initializeApp } from 'firebase-admin';

const serviceAccount: ServiceAccount = {
  projectId: 'a',
  clientEmail: '[email protected]',
  privateKey: '...'
}

export const createJWT = onCall(async () => {
  // const app = initializeApp(
  //   { serviceAccountId: { credential: credential.cert(serviceAccount) } },
  //   'a',
  // ); // << works
  const app = initializeApp(
    { serviceAccountId: '[email protected]' },
    'a',
  ); // << does not work, but fails on the next line
  const token = await auth(app).createCustomToken('test');
  return { token };
});

ruslan-simonenko avatar Sep 25 '24 06:09 ruslan-simonenko

Also getting this error, in a brand new project. Confirmed that the default service account has the right role/permissions. e.g.

firebase-adminsdk-[FOO]@[PROJECT_ID].iam.gserviceaccount.com and the role is Service Account Token Creator

And functions/src/index.ts:

import { onRequest } from 'firebase-functions/v2/https';

import { initializeApp } from 'firebase-admin/app';
import { getAuth } from 'firebase-admin/auth';

initializeApp();

const auth = getAuth();
const testId = 'test_id';

export const helloWorld = onRequest(async (_request, response) => {
  const token = await auth.createCustomToken(testId);
  response.send(token);
});

rttll avatar Nov 13 '24 06:11 rttll

@ruslan-simonenko @rttll

I also ran into this error. I tried updating the service account roles, but that didn’t fix it. I ended up solving it by specifying the credential in initializeApp.

Here’s how I fixed it:

In the Firebase console, go to Project settings -> Service accounts -> Generate new private key. Add the downloaded .json file to your project (make sure to add it to your .gitignore). Then, import the file and set it in initializeApp. Here’s how it looks for me:

import { initializeApp, cert } from "firebase-admin/app";
import serviceAccount from "/path/to/service-account.json";
import { ServiceAccount } from "firebase-admin/lib/app/credential";

initializeApp({
  credential: cert(serviceAccount as ServiceAccount),
});

This resolved the issue for me. Hope this helps in your cases as well!

alex-leshchuk avatar Nov 15 '24 01:11 alex-leshchuk

This helped me: https://stackoverflow.com/questions/75071759/google-cloud-storage-permission-denied

kuatek avatar Apr 30 '25 15:04 kuatek

For anyone else stumbling across this. I found V2 Cloud Functions use a different service account to that suggested in the documentation

I ran gcloud functions describe FUNCTION_NAME --region=YOUR_REGION

And saw that the service account being used was [email protected]

Applying the Service Account Token Creator permission to that SA solved the issue.

Also make sure you have enabled the IAM API.

I had been creating tokens for years with V1 functions, so the specified service account already had the correct permissions and I didn't realise I needed to enable a new API.

pklitscher avatar Jun 01 '25 21:06 pklitscher

@pklitscher Thanks for the warning, finally solved my token problem!

sotoasty avatar Jun 05 '25 20:06 sotoasty

@pklitscher YOU SAVED MY LIFE!!! THANKS A LOT!!!!!!!

mnls0115 avatar Jun 08 '25 07:06 mnls0115

@pklitscher You continue to save us, thank you! Had the IAM Service Account Credentials API enabled but no idea i needed the Identity and Access Management (IAM) API too,

seeweed12345 avatar Jun 23 '25 00:06 seeweed12345

@pklitscher heres another thank you!

Service Account Token Creator is the service account permission that is needed.

MrRoyce avatar Jul 02 '25 00:07 MrRoyce

This issue can be closed as @pklitscher’s answer appears to be correct.

JimTeva avatar Jul 11 '25 02:07 JimTeva