putObject with signed URL gets SignatureDoesNotMatch response
Closed this issue · 1 comments
afifsohaili commented
Hello, I'm using serverless-s3-local
(which uses this library underneath) to create an S3 signed URL for uploading from the browser. Here's a snippet of my backend code:
const initializeS3 = () => {
const isDevMode = process.env.NODE_ENV === 'dev';
const bucketUrl = isDevMode ? process.env.S3_LOCAL_BUCKET_URL : process.env.S3_BUCKET_URL;
if (!isDevMode) { // production, staging
return new AWS.S3();
}
return new AWS.S3({
endpoint: bucketUrl, // resolves to http://localhost:15004/<bucket-name>
s3BucketEndpoint: true,
accessKeyId: 'S3RVER',
secretAccessKey: 'S3RVER'
});
};
const createS3PresignedUrl = fileType => {
const s3 = initializeS3();
const Key = generateSaveKey(); // uuid()
const params = {
ACL: 'public-read',
Bucket: process.env.S3_BUCKET_NAME,
ContentType: 'image/jpeg',
Expires: 60,
Key
};
return new Promise((resolve, reject) => {
s3.getSignedUrl('putObject', params, (err, s3SignedUrl) => {
if (err) {
console.error(err);
reject(err);
return;
}
resolve({s3SignedUrl});
});
});
};
Then on frontend, I call this endpoint to get the s3SignedUrl
and do a PUT request to that returned URL.
// my s3rver is running on http://localhost:15004
// call backend to get the signed URL
// http://localhost:15004/<bucket-name>/<Key>?AWSAccessKeyId=S3RVER&Content-Type=image%2Fjpeg&Expires=1234&Signature=xyz123&x-amz-acl=public-read
const backendService = axios.create({baseURL: process.env.BASE_URL})
const {data: {s3SignedUrl}} = await backendService.post('/images', {}, {headers: {authorization: `Bearer ${jwtToken}`}});
// use the s3SignedUrl returned to do a PUT request
// file is an instance of File
await axios.put(s3SignedUrl, file, {headers: {'Content-Type': file.type}});
However, this resolves to the following error:
<?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 key and signing method.</Message>
<AWSAccessKeyId>S3RVER</AWSAccessKeyId>
<StringToSign>GET
image/jpeg
1558325033
x-amz-acl:public-read
/dev.jemput.co/44b5b78a-5f5e-4640-9831-6299a214ccb9.jpg</StringToSign>
<StringToSignBytes>47 45 54 0a 0a 69 6d 61 67 65 2f 6a 70 65 67 0a 31 35 35 38 33 32 35 30 33 33 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 2f 64 65 76 2e 6a 65 6d 70 75 74 2e 63 6f 2f 72 65 73 6f 75 72 63 65 73 2f 34 34 62 35 62 37 38 61 2d 35 66 35 65 2d 34 36 34 30 2d 39 38 33 31 2d 36 32 39 39 61 32 31 34 63 63 62 39 2e 6a 70 67</StringToSignBytes>
</Error>
This code works, however, on my staging and production instances, when it's accessing real S3 buckets.
Thoughts?
afifsohaili commented
Nevermind. I realize it's because of the s3BucketEndpoint: true
parameter. I don't know why I even had that in the first place. Removed it and it works fine now.