Search code examples
javafirebasegoogle-cloud-storagefirebase-admin

How can I download an object from Google Cloud Storage, through the Java Admin SDK?


I am currently developing a Java application that uses the Admin SDK to store and retrieve data from Google Firestore, but I also wanted to extend that use to Google Cloud Storage as well. When it comes to Firestore it is very clearly stated that access via Admin SDK ignores all Firebase Security rules and as a result, creating a Firebase App with my Service Account credentials is enough to grant access to all operations (If I've understood that correctly so far).

However when I try to download .png files that I've uploaded on Cloud Storage through Admin SDK I get the following error.

{
  "code" : 401,
  "errors" : [ {
    "domain" : "global",
    "location" : "Authorization",
    "locationType" : "header",
    "message" : "Anonymous caller does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist).",
    "reason" : "required"
  } ],
  "message" : "Anonymous caller does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist)."
}

Here is the code that produces this error.

public void downloadImage() {
        
        firebase.initializeFirebase();

        Storage storage = StorageOptions.newBuilder()
                                  .setProjectId(MY_FIREBASE_ID).build()
                                  .getService();
        
        Blob blob = storage.get(BlobId.of(STORAGE_ID, OBJECT_NAME));
        blob.downloadTo(Paths.get(PATH));
    }

The roles of the Service Account include Admin, Storage Admin, Storage Object Viewer, Storage Object Admin and a few others. The Firebase App is initialized in the firebase.initializeFirebase() method with the credentials that I mentioned above.

I thought the Admin SDK gave complete access to a Firebase App with only the appropriate IAM Roles. Searching for this error, I encountered a few ways that authenticate and allow access correctly but they all used Firebase CLI. But how can I do the same using only the Admin SDK and Java code?

As stated above, I've tried giving any Storage related roles to my Service Account and generated a new JSON file with my credentials. I then used the code above to try and download an image from Cloud Storage but to no avail. I even went as far as to allow public access to anyone to the Cloud Storage. I went to the Cloud Storage rules and enabled read, write access without any conditions. But still no progress. I searched this specific question that I am asking right now but the results didn't help me. Maybe I missed something, and in that case I'm sorry.


Solution

  • The error says you are not using any credentials in the request. If you want to authorize the request with env variables check out the docs to let the Storage service discover the credentials (outside of a Google cloud instance)

    MAKE SURE to add the necessary permissions to your Service Account and Bucket.

    1. For Service Account: Go to Google Cloud Console > IAM and Admin > Find your service account that you want to use > Edit Principal > Add Role > Choose your desired role (I chose Storage Admin so that this service account has full access to buckets and objects inside of them)
    2. For Bucket: Go to Google Cloud Console > Cloud Storage > Buckets > Select your bucket > More Actions (3 dots on the right) > Edit Access > Add Principal > In the Principal field, add your service account name/email and at the roles, set the same as you did for your Service Account (Storage Admin for example)

    If you have previously set up Firebase App and you have downloaded a JSON file with your credentials, you can use this JSON file to authorize your request to Cloud Storage, without setting up env variables like this:

    InputStream serviceAccount = new FileInputStream("your-path-to-json-credentials");
    
    Storage storage = StorageOptions.newBuilder().setProjectId(PROJECT_ID).setCredentials(GoogleCredentials.fromStream(serviceAccount)).build().getService();
    

    If on the other hand you don't have the credentials in a JSON format and you just need Cloud Storage access, navigate to the Google Cloud Console > IAM and Admin > Service Accounts > Select your service account > More Options (3 dots on the right) > Manage Keys > Add Key > Create New Key > JSON Format. Now download the JSON file and use that path as shown above in code.

    Now, whenever you try to download, create or delete objects from your desired Bucket, Google knows that the request is coming from your Service Account.