Search code examples
javascriptnode.jsfirebasegoogle-cloud-storage

Why are files in Cloud Storage not publicly available?


I am using Firebase and the Cloud Storage to host images.

On my backend, this is how I write images:

const bucket = getStorage().bucket('gs://project-name.appspot.com');
const bucketFile = bucket.file(path);
await bucketFile.save(imageBuffer);

I am storing the bucketFile.publicUrl() in the database which is what clients use to display the image.

and these are the deployed storage.rules:

rules_version = '2';

// Craft rules based on data in your Firestore database
// allow write: if firestore.get(
//    /databases/(default)/documents/users/$(request.auth.uid)).data.isAdmin;
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read: if true;
    }
  }
}

Still, I cannot access the file.

That being said, I seem to be able to access them if I additionally call

await bucketFile.makePublic();

However, I don't think this is ideal having to call this for each and every file. Call me picky, but I'd assume that this "simply works" based on my storage.rules.

Are my rules wrong or am I missing something?


Solution

  • Firebase security rules have absolutely no effect on the public URLs that you get from publicUrl(). They only affect access coming from the Firebase web and mobile SDKs, typically by users who are also signed in with Firebase Auth. It seems you're not doing either of those things.

    Since Firebase rules don't apply here, you will have to use the GCP documentation about making data public and ignore everything else related to Firebase. It seems like you're asking how to make an entire bucket publicly readable rather than just a single object. The relevant code from that section for nodejs is:

    // The ID of your GCS bucket
    // const bucketName = 'your-unique-bucket-name';
    
    // Imports the Google Cloud client library
    const {Storage} = require('@google-cloud/storage');
    
    // Creates a client
    const storage = new Storage();
    
    async function makeBucketPublic() {
      await storage.bucket(bucketName).makePublic();
    
      console.log(`Bucket ${bucketName} is now publicly readable`);
    }
    
    makeBucketPublic().catch(console.error);
    

    Note also that you're suppose to pass the name of the bucket without gs:// to storage.bucket().