Search code examples
node.jsreactjsfile-uploadgoogle-cloud-storage

MalformedSecurityHeader error in signed url - Header was included in signedheaders, but not in the request


I am trying to use Signed URLs to upload the files through the Google app engine using react and node. I was able to generate signed URLs but when getting a CORS error in the console. When I open the signed URL in the browser getting the following error in return.

<Error>
<Code>MalformedSecurityHeader</Code>
<Message>Your request has a malformed header.</Message>
<ParameterName>content-type</ParameterName>
<Details>Header was included in signedheaders, but not in the request.</Details>
</Error>

Here is my node js code to generate Signed Url

async function generateV4UploadSignedUrl() {
          const options = {
            version: "v4",
            action: "write",
            expires: Date.now() + 15 * 60 * 1000, // 15 minutes
            contentType: "audio/wav",
          };

          // Get a v4 signed URL for uploading file
          const [url] = await gcTempFiles
            .file(gcFileName)
            .getSignedUrl(options);

          console.log("Generated PUT signed URL:");
          console.log(url);
          console.log("You can use this URL with any user agent, for example:");
          console.log(
            "curl -X PUT -H 'Content-Type: application/octet-stream' " +
              `--upload-file my-file '${url}'`
          );

          res.status(200).json({
            success: true,
            msg: `url created`,
            data: { url},
          });
        }

Here is my frontend code in react app


let file = files.map((f) => f.file);

console.log(file)

let xhr = new XMLHttpRequest();
        xhr.open('PUT', signedurl, true);
        xhr.setRequestHeader("Content-type", "application/octet-stream");
        xhr.onload = function (response) {
          console.log('on-load', response);
        };

        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
        
            if (xhr.status === 200) {
              console.log("Status OK")
        
            } else {
              console.log("Status not 200")
            }
          }
        };

        xhr.onerror = function (response) {
          console.log("Response error", response)
        };

        xhr.upload.onprogress = function (evt) {
          // For uploads
          if (evt.lengthComputable) {
            var percentComplete = parseInt((evt.loaded / evt.total) * 100);
            console.log("progress", percentComplete)
        
          }
        }
        xhr.send(file);

Also, I have set all the CORS Policy on bucket level using the following command gsutil cors set cors.json gs://bucket name

cors.json file

[{"origin": ["*"],
"responseHeader": ["X-Requested-With", "Access-Control-Allow-Origin", "Content-Type"],
"method": ["GET", "HEAD", "POST"],
"maxAgeSeconds": 3600}]

Again I am building the web application on the google app engine. And need to be signed URLs to workaround 32MB restrction. Can someone point me out what I am doing wrong? Therefore, Any helpful suggestion is highly appreciated.


Solution

  • I have resolved the error using configure the CORS on the bucket using the following code. It's working very well.

    async function configureBucketCors() {
        await storage.bucket(bucketName).setCorsConfiguration([
          {
            maxAgeSeconds: 3600,
            method: ["PUT", "GET", "HEAD", "DELETE", "POST", "OPTIONS"],
            origin: ["*"],
            responseHeader: [
              "Content-Type",
              "Access-Control-Allow-Origin",
              "x-goog-resumable",
            ],
          },
        ]);
      }
      configureBucketCors().catch(console.error);