35

I'm trying to use file.getSignedUrl() to get the download URL from Firebase Storage via Google Cloud Functions (Nodejs). I'm getting this error in the Cloud Functions console:

{ SigningError: A Forbidden error was returned while attempting to retrieve an access token for the Compute Engine built-in service account. This may be because the Compute Engine instance does not have the correct permission scopes specified. Permission iam.serviceAccounts.signBlob is required to perform this operation on service account projects/myapp-cd94d/serviceAccounts/myapp-cd94d@appspot.gserviceaccount.com.
    at SigningError (/user_code/node_modules/@google-cloud/storage/build/src/file.js:58:9)
    at authClient.sign.then.catch.err (/user_code/node_modules/@google-cloud/storage/build/src/file.js:1019:22)
    at process._tickDomainCallback (internal/process/next_tick.js:135:7) name: 'SigningError' }

I copied the code from the Add the Firebase Admin SDK to Your Server documentation. I have my serviceAccountKey.json in my functions folder. firebase deploy isn't given me the error

Error parsing triggers: Cannot find module 'serviceAccountKey.json'

so I must have the right path to my serviceAccountKey.json. I even generated a new private key, that didn't fix the problem. I have firebase-admin 6.1.0 and firebase-tools 6.1.0. Here's the relevant parts of my code:

const admin = require('firebase-admin');
var serviceAccount = require("./myapp-cd94d-firebase-adminsdk-1234x-sEcReT.json");

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://myapp-cd94d.firebaseio.com"
});

...

const config = {
  action: 'read',
    expires: '03-17-2025'
  };

file.getSignedUrl(config).then(function(data) {
    const url = data[0];
    console.log(url);
  })
  .catch(function(error) {
    console.error(error);
  })

I saw that Doug Stevenson's answer has different code but it appears to be equivalent to the code in the documentation.

Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100

2 Answers2

127

The answer has to do with Cloud Identity and Access Management. First, go to your Google Cloud Platform IAM & admin page. You'll see various service accounts. Look for the service account that looks like myapp-cd99d@appspot.gserviceaccount.com. It should say App Engine default service account in the Name column. (If an error message referenced a different service account, find that service account.)

In the Role column, you may or not see some roles. If you're getting a SigningError message, the Role column is missing the role Service Account Token Creator. Check the checkbox to the left of myapp-cd99d@appspot.gserviceaccount.com to select the service account, and then click the pencil to the right to edit it. In the next screen, click +ADD ANOTHER ROLE. Scroll down to Service Accounts, select Service Account Token Creator, and save. Now you should see Service Account Token Creator in the Roles column for App Engine default service account. Now you have permission to create signed tokens.

Next, repeat these steps and add a role for Storage Object Creator. This will allow you to run getSignedURL().

You could save alternatively assign Service Account Admin and Storage Admin, which include the Service Account Token Creator and Storage Object Creator roles respectively, plus other roles.

Now, if you instead got a SingingError message, it might be because you're warbling Bruce Springsteen's "Glory Days" out of tune. :-)

Thomas David Kehoe
  • 10,040
  • 14
  • 61
  • 100
  • 17
    what i wanna know is why this isnt taken care of / documented – galki Dec 04 '18 at 22:50
  • 1
    "By default the service account ID you get from the Firebase console, and the ones auto-discovered via Cloud Functions do not have these permissions" from a [medium article](https://medium.com/@hiranya911/firebase-create-custom-tokens-without-service-account-credentials-d6049c2d2d85) – galki Dec 05 '18 at 13:04
  • @Thomas David Kehoe. Hii Thomas. I stored a file in firebase cloud storage in the format of .txt. using an https api using Node.js. That file contains some data regarding the user details. I want to download that file along with data in that file and send to another server. i got the url of that stored file. How to get the file along the data? Do you have any idea in that? – vijju Mar 07 '19 at 10:34
  • vijju, you use the downloadURL to get the file from Storage. It's available in the console and when you upload the file to Storage. – Thomas David Kehoe Mar 16 '19 at 01:21
  • 1
    @ThomasDavidKehoe how to do this using gcloud, I saw this link https://cloud.google.com/iam/docs/granting-roles-to-service-accounts but would you please show me an example ? – ilya Apr 10 '19 at 16:16
  • 1
    Thomas David Kehoe's answer got me pointed in the right direction but I wasn't able to find the Storage Object Creator role? I came across this SO answer which ended up solving the problem of giving my firebase admin SDK the permission it needed to create a SignedURL: https://stackoverflow.com/a/53422174/10541855 Also I found this medium article quite helpful too https://medium.com/@hiranya911/firebase-create-custom-tokens-without-service-account-credentials-d6049c2d2d85 – devinm Aug 05 '20 at 18:10
  • How can I set these roles for Google Kubernetes Cluster. Will these roles be part of permissions which we set during creation of cluster? – smnth90 Oct 21 '20 at 09:10
  • going to https://console.cloud.google.com/iam-admin/iam worked for me, as I was getting `Role cannot be edit as it is inherited from another resource` – prisar Mar 05 '21 at 03:19
  • This totally sorted out the issue for me, but just to add a couple of things ... if you are managing the project for a client (even if you have owner permissions on the Firebase account) you will not be able to edit the roles unless you login with the main user account. Also, note that these URLs expire so not useful if you want to store them for permanent use in an app. Best to make the file public and then just build the public URL without the tokens and expiry. – Lee Probert May 17 '21 at 08:50
  • **For people looking for Storage Object Creator** go to `Cloud Storage` -> `Storage Object Creator` – Joe Moore Aug 17 '22 at 11:04
  • 1
    Lol I literally was listening to Bruce Sp. Glory Days while reading this... – Miguel Puig Feb 16 '23 at 18:16
6

In my case I had enable Identity and Access Management (IAM), the url is the following one:

https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project="YOUR PROJECT NAME"

Pol Fernández
  • 1,198
  • 9
  • 14
  • 1
    This worked for me finally. `https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project=PROJECT_NUMBER`. PROJECT_NUMBER is from firebase project settings. A 12 digit number for my project. – akhil xavier Jul 28 '21 at 07:57
  • This is the link that worked for me: `https://console.cloud.google.com/apis/library/iamcredentials.googleapis.com?project=PROJECT_NAME_OR_PROJECT_NUMBER` – SuttonY Aug 16 '23 at 15:42