DigitalJohn
DigitalJohn

Reputation: 281

Can't get node.js to return a valid signed PUT URL

Im tearing my hair out trying to get an S3 Direct Client Side PUT operation to work.

We have a version of the backend code working on Python without any issues(so we know the frontend works just fine) and we are trying to port the backend to Node.JS.

I have an endpoint setup that that returns a signed PUT URL, here is the code:

var objectKey = req.query.s3_object_name;
var objectType = req.query.s3_object_type;

var params = {
    Bucket: s3Bucket,
    Key: objectKey,
    // ContentType: objectType, //(I have tried with and without this)
    Expires: 60
};
s3.getSignedUrl('putObject', params, function(err, signedUrl){
    if(err){
        res.send(400);
    }else{
        res.end(JSON.stringify({
            signed_request: signedUrl,
            url: "http://"+s3Bucket+".s3.amazonaws.com/"+objectKey
        }));
    }
});

Unfortunately Amazon always returns the following error:

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

Has anybody successfully got the JavaScript aws-sdk to perform this task successfully? Any pointers? I have double and triple checked my AWS Key and Secret.

Regards:

John Chipps-Harding

Upvotes: 1

Views: 1957

Answers (1)

Michael - sqlbot
Michael - sqlbot

Reputation: 179194

S3 seems to be giving you the answer in the error message, though you may not be recognizing it as such.

<StringToSign>PUT

image/jpeg
1390733729
x-amz-acl:public-read
/arenaupload/vV61536.jpg</StringToSign>

For any given request, there is only exactly one possible valid "string to sign," and if you don't (or the SDK doesn't) start with that string, then, of course, it won't work.

The error response from S3 isn't giving you the string you actually tried to sign, because it doesn't know that information. It's giving you the canonical version of the string you should have tried to sign, based on the request it is rejecting.

Your workaround is working, because you've added x-amz-acl:public-read to the string-to-sign... but your code, in the original question, isn't specifying that information anywhere anywhere. I don't know whether the format the JS-SDK expects is ACL: "public-read" or exactly what it will want to see, presumably in params, to make this work but it seems apparent enough that you aren't asking the SDK to sign a request that precisely matches the actual upload that you're subsequently attempting to do.

Upvotes: 6

Related Questions