Search code examples
javascriptnode.jsfirebasegoogle-cloud-platformcors

How can I setup CORS properly to upload file with a presignedUrl to firebase storage?


I have been trying to upload an image directly from my react spa to my firebase bucket but I can't configure the cors properly.

I set the cors configuration in muy bucket using the GCP console, as:

[
  {
    "origin": ["http://localhost:3000"],
    "method": ["PUT"],
    "responseHeader": [
        "Content-Type",
        "Access-Control-Allow-Origin",
        "x-goog-resumable"],
    "maxAgeSeconds": 3600
  }
]

but it doesn't works in the browser, instead in postman it works as expected.

My app implementation works as follows:

First I generate the presigned url in my node api as:

const admin = require('firebase-admin');
const uuid = require('uuid');

admin.initializeApp({
    credential: admin.credential.applicationDefault(),
    storageBucket: "<BUCKET-NAME>"
});

var bucket = admin.storage().bucket();

const generateSignedUrl = async (userId) => {
    const key = `${userId}/${uuid.v1()}.jpeg`;
    const options = {
        version: 'v4',
        action: 'write',
        expires: Date.now() + 5*60 * 1000,
        contentType: 'image/jpeg'
    };

    const [ url ] = await bucket
        .file(key)
        .getSignedUrl(options);

    return [url, key];
};

Then in the spa I use axios to perform the put request in order to upload the file using the presignedUrl provided from my node api.

 const config = {
   headers: { 'Content-Type': 'application/octet-stream' }
 };
 await axios.put(presignedUrl, file, config);

But the CORS error appears:

Access to XMLHttpRequest at 'https://storage.googleapis.com/...' from origin 'http://localhost:3000' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Solution

  • Finaly I resolved my issue, I post the answer here, maybe it could help someone else. I had two issues.

    • First, I had been sending an authorization header for all the requests and due to my CORS configuration firebase doesn't profile it correctly. I decided to omit the auth header for the presigned upload request as:
        delete axios.defaults.headers.common["x-auth-token"];
    
    • Second, the contentType header was set up incorrectly, but due to chrome doesn't show the options requests in the network tab by default (out-of-blink-cors), the No 'Access-Control-Allow-Origin' header is present.. error always appeared. Once I figured out the correclty error thanks to firefox, I change the options for signedUrl generation as:
        const options = {
            version: 'v4',
            action: 'write',
            expires: Date.now() + 5*60 * 1000,
            contentType: `image/${type}`
        };
    

    where type is the type of the image to upload, provided from the spa.

    The cors configuration works as expected.

    [{
        "origin": ["http://localhost:3000"],
        "method": ["PUT"],
        "responseHeader": [
            "Content-Type"
        ],
        "maxAgeSeconds": 3600
    }]
    

    To make changes to your selected bucket, you can use this command:

    gcloud storage buckets update gs://{BUCKET_NAME} --cors-file={CORS_FILE}