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&X-Goog-Credential=service_account_email%2F20200727%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200727T075531Z&X-Goog-Expires=518401&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?