11
votes

Title says everything. Here is my code;

I'm using node-formidable for files.

form.on("end",function(field, file){
        params.Body = fs.createReadStream(params.filePath)
        delete params.filePath;
        S3.getSignedUrl('putObject',params, function(err, url) {
            if(err){console.log(err);}
            console.log(url);
        });
    })

After successful upload, url variable returns s3 url, something like this;

https://bucket-name.s3.amazonaws.com/746575308_8c081369df.jpg?AWSAccessKeyId=[key]&Expires=[date]&Signature=[signature]&x-amz-acl=public-read

But still getting SignatureDoesNotMatch error. In description says

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

Here is my parameters

params = {
    Bucket:"bucketname",
    Key: file.name,
    ACL: 'public-read'
}

What am i missing?

2

2 Answers

14
votes

I ran into the same problem while using S3.getSignedUrl('putObject', serverside, and then trying to use that url clientside.

What I noticed in my case, which might be relevant to yours, is that signatures created with all the S3.getSignedUrl take into account request headers. So if you are generating a URL, it will fail with the same error message you received unless sent with the same headers.

One example of a failure: Generated like this..

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000 };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });

The following request fails when a using that same url generated. This request was made from a browser.

RequestMethod: Put
Headers: {
    Accept:*/*
    Accept-Encoding:gzip, deflate, br
    Accept-Language:en-US,en;q=0.9
    Connection:keep-alive
    Content-Length:11768
    Content-Type:application/x-www-form-urlencoded; charset=UTF-8
}

And the difference is that the signature above created doesn't include content-type, where the request does specify a content-type. Params need to match headers, or the error thrown will be signature doesn't match.

Successful example below:

var params = { Bucket: 'YourBucket', Key: 'uniqueFileKey', Expires: 10000, ContentType: 'application/x-www-form-urlencoded; charset=UTF-8' };
s3.getSignedUrl('putObject', params, function (err, url) {
      if(err){
        return cb(err);
      }
      return cb(null, url)
    });
-3
votes

Give this a try. You need to upload the object, then generate the signed url against an existing object.

var s3bucket = 'somebucket';
var s3Key = '/some/key',
var body = fs.createReadStream('/some/local/file.txt');

var params = {
    Bucket: s3bucket,
    Key: s3Key,
    Body: body
};
s3.upload(params, function(err) {
    if (err) {
        cb_1(err);
    } else {
        var params = {
            Bucket: s3bucket,
            Key: s3Key,
            Expires: parseInt(ttl)
        };
        s3.getSignedUrl('getObject', params, function(err, url) {
            if (err) {
                console.log(err);
            } else {
                console.log(err);
            }
        });
    }
});