0

Form-based storage upload system stopped working and returns the following response with status 403:

<?xml version='1.0' encoding='UTF-8'?>
<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>GOOG4-RSA-SHA256
20200727T075531Z
20200727/auto/storage/goog4_request
932e77c1635af98f9403613333d58ef0a3c3ebc945ea76c12564639c6f65b941</StringToSign>
    <CanonicalRequest>POST
/mybucket/images/filename.jpg
X-Goog-Algorithm=GOOG4-RSA-SHA256&amp;X-Goog-Credential=service_account_email%2F20200727%2Fauto%2Fstorage%2Fgoog4_request&amp;X-Goog-Date=20200727T075531Z&amp;X-Goog-Expires=518401&amp;X-Goog-SignedHeaders=content-type%3Bhost
content-type:multipart/form-data; boundary=--------------------------112107901923545012948853
host:storage.googleapis.com

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

Everything worked flawlessly in the last 4 months. There are no changes in service account permissions. The latest thing I did was enable CORS for my bucket, and even after that everything worked for about 2-3 weeks.

Request params are generated on backend in node.js using @google-cloud/storage library, particularly methods getSignedPolicy and getSignedUrl. Then all the data is passed back to client, which makes POST request using values from server.

Url and policy generation code:

const config = {
  keyFilename: 'path/to/credentials.json',
  projectId: 'project-id',
};
const storage = new Storage(config);
const bucket = storage.bucket('mybucket');
 
// policy generation
const [policy] = await bucket
  .file(filePath)
  .getSignedPolicy({
    expires: Date.now() + 6 * 86400 * 1000,
    equals: [
      ['$x-goog-meta-myapp-content-type', contentType],
    ],
    contentLengthRange: {
      min: 1,
      max: options.maxContentLength
    },
    acl: 'public-read'
  });
 
// upload url generation
const [uploadUrl] = await bucket
  .file(filePath)
  .getSignedUrl({
    version: 'v4',
    action: 'write',
    contentType: 'application/octet-stream',
    expires: Date.now() + 6 * 86400 * 1000,
  });
 
return {
  imageId,
  policy,
  key: filePath,
  uploadUrl,
  bucket: bucket.name,
  acl: 'public-read',
  accessId: (await storage.authClient.getCredentials()).client_email,
  policy: policy.base64,
  signature: policy.signature,
};

Client request code:

const body = new FormData();
body.append('key', uploadData.key);
body.append('bucket', uploadData.bucket);
body.append('X-Goog-Meta-Myapp-Content-Type', mime);
body.append('GoogleAccessId', uploadData.accessId);
body.append('acl', uploadData.acl);
body.append('policy', uploadData.policy);
body.append('signature', uploadData.signature);
body.append('file', blob, filename);
 
// POST request to uploadData.uploadUrl using fetch/xhr or something else.
// 'Content-Type' is set to 'multipart/form-data' with proper boundary

First time it happened about 2 days ago, and it's been like that ever since. Is there a problem with the approach itself?

German Gorodnev
  • 102
  • 1
  • 7
  • Did you try to include in the call the HTTP Method and Content-Type headers? Maybe this could help – Andie Vanille Jul 27 '20 at 22:42
  • @AndieVanille Yes, content-type is set to `multipart/form-data; boundary=` – German Gorodnev Jul 28 '20 at 03:30
  • I found this [post](https://stackoverflow.com/questions/62353634/cors-policy-with-google-storage-allows-from-my-origin-but-no-access-control-al) with similar scenario and the same issue, maybe this one could help you – Andie Vanille Aug 01 '20 at 01:08

0 Answers0