Getting 403 (Forbidden) when uploading to S3 with a signed URL

Glenn picture Glenn · Oct 22, 2017 · Viewed 19.5k times · Source

I'm trying to generate a pre-signed URL then upload a file to S3 through a browser. My server-side code looks like this, and it generates the URL:

let s3 = new aws.S3({
  // for dev purposes
  accessKeyId: 'MY-ACCESS-KEY-ID',
  secretAccessKey: 'MY-SECRET-ACCESS-KEY'
});
let params = {
  Bucket: 'reqlist-user-storage',
  Key: req.body.fileName, 
  Expires: 60,
  ContentType: req.body.fileType,
  ACL: 'public-read'
};
s3.getSignedUrl('putObject', params, (err, url) => {
  if (err) return console.log(err);
  res.json({ url: url });
});

This part seems to work fine. I can see the URL if I log it and it's passing it to the front-end. Then on the front end, I'm trying to upload the file with axios and the signed URL:

.then(res => {
    var options = { headers: { 'Content-Type': fileType } };
    return axios.put(res.data.url, fileFromFileInput, options);
  }).then(res => {
    console.log(res);
  }).catch(err => {
    console.log(err);
  });
}

With that, I get the 403 Forbidden error. If I follow the link, there's some XML with more info:

<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
...etc

Answer

Michael - sqlbot picture Michael - sqlbot · Oct 22, 2017

Your request needs to match the signature, exactly. One apparent problem is that you are not actually including the canned ACL in the request, even though you included it in the signature. Change to this:

var options = { headers: { 'Content-Type': fileType, 'x-amz-acl': 'public-read' } };