6
votes

I'm new to CORS configuration and trying to figure this out, but my set up looks like it is right according to the documentation. I'm hoping you can help me see what I've missed. My code is trying to upload (PUT) a file directly to google storage using a signed url.

Access to XMLHttpRequest at 'https://storage.googleapis.com/herdboss-dev.appspot.com/uploads/152/152-owner-152-61.jpg?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Go...' from origin 'https://herdboss-dev.appspot.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I've set the CORS policy on my Google Storage Bucket (added line breaks for readability):

% gsutil cors get gs://herdboss-dev.appspot.com               
[{
"maxAgeSeconds": 3600, 
"method": ["GET", "HEAD", "PUT"], 
"origin": ["https://herdboss-dev.appspot.com"], 
"responseHeader": ["X-Requested-With", "Access-Control-Allow-Origin", "Content-Type"]
}]

According to my chrome inspector, the pre-flight request happens and returns the proper methods and headers (as far as I can tell). Also, as a side note, I noticed the PUT shows up in my Chrome inspector before the OPTIONS, but the timestamps show the OPTIONS is sent first.

enter image description here

When the OPTIONS call happens, Google Storage correctly responds and says it can support my request methods GET, HEAD, PUT and origin https://herdboss-dev.appspot.com:

enter image description here


THE PUT REQUEST

However, when the PUT happens, Google Storage does not respond with the proper Access-Control-Allow-Origin header:

enter image description here

What am I missing or doing wrong? I'm able to use these signed urls to PUT files directly into my Google Storage bucket when I make a direct http call, so I know the signed urls work. I'm just running into this CORS problem. It's a new thing for me, I haven't dealt with it before, but it seems like I'm setting all of the things I should be setting.

**EDIT 1:

I tried setting my allowed origins to * just as a test and I was still rejected with the same No 'Access-Control-Allow-Origin' header is present on the requested resource. The Chrome inspector showed the OPTIONS response did send back my * for allowed origins, but it still didn't work and still gave the same error.

1

1 Answers

10
votes

Figured it out.

Short version: Chrome's error message about CORS was a red herring. Google Storage was rejecting the request which was being interpreted as a CORS violation.

Long version:

I noticed in Chrome's inspector that the response to my PUT request had a content length of 862 characters, but Chrome wasn't showing me any response. I opened Firefox and tried the upload process again and it failed with the same error messages about CORS and Access-Control-Allow-Origin. However, in Firefox's network inspector, I was able to see the raw response body!

<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
    <StringToSign>.....</StringToSign>
    <CanonicalRequest>PUT
/herdboss-dev.appspot.com/uploads/152/152-owner-152-72.png
X-Goog-Algorithm=GOOG4-RSA-SHA256&amp;....;X-Goog-SignedHeaders=content-type%3Bhost
content-type:image/png
host:storage.googleapis.com

content-type;host
UNSIGNED-PAYLOAD
    </CanonicalRequest>
</Error>

My theory is that when Chrome and Firefox do the pre-flight CORS checking and then issue the PUT request and receive a 403 response code that it is responding as if it is a CORS problem even though the problem was not CORS related and the preflight response DID actually have the Access-Control-Allow-Origin header.

From here, I was able to quickly diagnose the problem by checking my url signing code against the request. I noticed that when created the signed upload url that I was requiring a Content-type of application/octet-stream but when the javascript in the browser was doing the PUT request that it was setting the Content-type to image/png. I updated my javascript to force the Content-type to match application/octet-stream and then the request succeeded.