Jquery – Presigned AWS S3 PUT url fails to upload from client using jquery

amazon s3amazon-web-servicesjquerynode.js

Just started working with the node.js aws client to generate a presigned url and send it to the browser for the user to upload the file, but I get the following message:

SignatureDoesNotMatch The request signature we calculated does not
match the signature you provided. Check your key and signing method.

I have references quite a few links and it seems really basic, but I seem to be failing

https://github.com/aws/aws-sdk-js/issues/251

Direct Browser Upload to S3 with Meteor, jQuery and the AWS SDK

https://forums.aws.amazon.com/thread.jspa?messageID=556839

Either, I am completely stupid or the sdk is really difficult to use

node:

var putParams = {
      Bucket: config.aws.s3UploadBucket,
      Key: filename,
      ACL: 'public-read',
      Expires: 120,
      Body: '',
      ContentMD5: 'false'
    };
s3.getSignedUrl('putObject', putParams, function (err, url) {
  if (!!err) {
    console.error(err);
    res.json({error: ''});
    return;
  }

  res.json({
    'awsAccessKeyId': config.aws.accessKeyId,
    's3bucket': config.aws.s3UploadBucket,
    's3key': filename,
    's3policy': s3policy.policy,
    's3signature': s3policy.signature,
    'url': url
  });
});

client:

  var fd = new FormData();
      fd.append('file', file);
    return new RSVP.Promise(function(resolve, reject) {
            $.ajax({
              url: uploadObj.url,
              data: fd,
              processData: false,
              contentType: false,
              crossDomain: true,
              type: 'PUT',
              success: function(json, textStatus, jqXhr){
                console.log(json);
                resolve(json);
              },
              error: function(jqXhr, textStatus, errorThrown){
                reject({ jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown});
              }
            });
          });

UPDATE: In response to some of the comments, I did put in a valid CORS for the bucket.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Authorization</AllowedHeader>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Best Answer

I've been fighting this as well. This is what worked for me and I was getting the exact same error as you.

On the server side, I'm using AWS-SDK for nodejs

var params = {
    Bucket: "bucketname",
    Key: "filename", 
    ContentType: "multipart/form-data"
}
var url = s3.getSignedUrl('putObject', params, function(err, url) { 
    console.log(url);
}

Client Side

$.ajax({
    method: "PUT",
    headers: {"Content-Type": "multipart/form-data"},
    processData: false,
    url: "http://AWSURL?AWSAccessKeyId..."
})

Your cors looks right to me, the key was ensuring that the headers for Content-Type matched exactly

Related Topic