0
votes

I have a firebase function which I want to permit write access to cloud storage. I believe I need to setup a service account with those permissions, and then grant them programmatically inside my function, but I'm confused how to do this.

The firebase function writes a file to a bucket on a trigger. The storage settings for the firebase storage are set to the default, which means they require the client to be authenticated:

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

In this document (https://cloud.google.com/functions/docs/concepts/iam), under "Runtime service account", I see this:

At runtime, Cloud Functions uses the service account [email protected], which has the Editor role on the project. You can change the roles of this service account to limit or extend the permissions for your running functions.

When it says "runtime," I'm assuming this means the firebase function runs within a context of that service account and the permissions granted to it. As such, I'm assuming I need to make sure the permissions of that service account have write access, as I see from this link (https://console.cloud.google.com/iam-admin/roles?authuser=0&consoleUI=FIREBASE&project=blahblah-2312312).

IAM permissions

I see the permission named storage.objects.create and would assume I need to add this to the service account.

To investigate the service account current settings, I ran these commands:

$ gcloud iam service-accounts describe [email protected]
displayName: App Engine default service account
email: [email protected]
etag: BwVwvSpcGy0=
name: projects/blahblah-2312312/serviceAccounts/[email protected]
oauth2ClientId: '98989898989898'
projectId: blahblah-2312312
uniqueId: '12312312312312'
$ gcloud iam service-accounts get-iam-policy [email protected]
etag: ACAB

I'm not sure if there is a way to get more details from this, and unsure what etag ACAB indicates.

After reviewing this document (https://cloud.google.com/iam/docs/granting-roles-to-service-accounts) I believe that I need to grant the permissions. But, I'm not entirely sure how to go from the JSON example and what structure it should be and then associate the policy, or if that is even the correct path.

{
    "bindings": [
    {
        "role": "roles/iam.serviceAccountUser",
        "members": [
            "user:[email protected]"
        ]
    },
    {
        "role": "roles/owner",
        "members": [
            "user:[email protected]"
        ]
    }
    ],
    "etag": "BwUqLaVeua8=",
}

For example, my questions would be:

  • Do I need to make up my own etag?
  • What email address do I use inside the members array?

I see this command listed as an example

gcloud iam service-accounts add-iam-policy-binding \
    [email protected] \
    --member='user:[email protected]' --role='roles/editor'

What I don't understand is why I have to specify two quasi-email addresses. One is the service account, and one is the user associated with the service account. Does this mean that user [email protected] can operate under the credentials of the service account? Can I just have the service account on its own have permissions which I use in my cloud function?

Is there a simpler way to do this using only the command line, without manually editing JSON?

And, then once I have my credentials properly established, do I need to use a JSON service account file as many examples show:

var admin = require('firebase-admin');
var serviceAccount = require('path/to/serviceAccountKey.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});

Or, can I just make a call to admin.initializeApp() and since "... at runtime, Cloud Functions uses the service account [email protected]..." the function will automatically get those permissions?

1
You should be able to initialize the admin sdk with no args.Doug Stevenson
So, I have this require('firebase-admin').initializeApp(); right before my call to functions.firestore.document(...) and I'm still getting this ApiError: [email protected] does not have storage.objects.create access to signatures/98989123KJLKJAS-KJLJLKJL8722.pngxrd
That's odd. When you init with no args, it uses the default service account for the project. That should be able to read and write the default storage bucket for a project. I do this all the time.Doug Stevenson
I did not (AFAIK) change the default permissions for the service account, so I don't think I removed that accidentally. Is storage.objects.create always available? I wish there were a way to easily see the permissions for a service account inside the firebase console on the web.xrd
Maybe there is something else going on. I'll try doing a really simple function trigger and see if perhaps my code elsewhere is getting in the way. But, it is already a really simple function, and it definitely fails when it gets to the upload call.xrd

1 Answers

0
votes

The issue was (as documented here: How to write to a cloud storage bucket with a firebase cloud function triggered from firestore?) that I had incorrectly specified the first parameter to the bucket as a subdirectory inside the bucket and not as just a bucket. This meant storage thought I was trying to access a bucket which did not exist, and I got the permissions error.