Here is a simple test case that looks like it should allow a simple object PUT via the SDK getSignedURL, but it fails with 'The request signature we calculated does not match the signature you provided. Check your key and signing method.' . What could be going wrong here that would cause that? Is anyone else getting a result like this?
var req = require('request');
var fs = require('fs');
var AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: "AKIAI3OVMHQEQKXSECPA",
secretAccessKey: "MY_SECRET_KEY"
});
//taken directly from http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-examples.html
var s3 = new AWS.S3();
var params = {Bucket: 'aws4test', Key: 'excalibur/items/stark.jpg'};
var url = s3.getSignedUrl('putObject', params);
console.log(url);
// url is https://aws4test.s3.amazonaws.com/excalibur/items/stark.jpg?AWSAccessKeyId=AKIAI3OVMHQEQKXSECPA&Expires=1455649657&Signature=REMOVED_THIS_FOR_SAFETY
fs.createReadStream('./stark.jpg').pipe(req({
method: "PUT",
url: url
}, function(err, res, body){
console.log(body);
}));
RESULT:
<?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>AKIAI3OVMHQEQKXSECPA</AWSAccessKeyId>
<StringToSign>PUT
image/jpeg
1455649657
/aws4test/excalibur/items/stark.jpg
</StringToSign>
<SignatureProvided>REMOVED_THIS_FOR_SAFETY</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 69 6d 61 67 65 2f 6a 70 65 67 0a 31 34 35 35 36 34 39 36 35 37 0a 2f 61 77 73 34 74 65 73 74 2f 65 78 63 61 6c 69 62 75 72 2f 69 74 65 6d 73 2f 73 74 61 72 6b 2e 6a 70 67
</StringToSignBytes>
<RequestId>ED433CAC6D8F41CB</RequestId>
<HostId>
TrO6AUHRlWzqpiT3NdarXWfVzY1mL4YCSZ8vL+P9AdRV0K3skPjm4T+mAmIy+S8P727ah3Vy2EU=</HostId>
</Error>
@raffi-minassian
There are two changes you'll need to make so that you can use streams and request to upload a file using a presigned url.
First, request is using chunked uploads when it is given a stream as the body to upload. Signature Version 4 needs to be used when generating the presigned url to support this. When instantiating your s3 client, pass in the signatureVersion: 'v4' parameter:
var s3 = new AWS.S3({
signatureVersion: 'v4'
});
request also automatically sets the Transfer-Encoding header when using streams, but S3 needs to know what the final content-length is. In the options you pass into request, you'll need to supply the Content-Length header.
var stats = fs.statSync('/path/to/file');
fs.createReadStream('/path/to/file').pipe(request({
method: 'PUT',
url: url,
headers: {
'Content-Length': stats['size']
}
}, function (err, res, body) {
console.log(body);
}));
There's some additional information here:
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
Let me know if this helps!
Thanks @chrisradek !!
With some help from AWS support and further digging I have found that the problem seems to be something in the way request is doing the PUT much as you described. The following code DOES work, so I'm closing this as it does not seem to have anything to do with aws-sdk not working.
var req = require('request');
var fs = require('fs');
var AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: "MYKEY",
secretAccessKey: "MYSECRET"
});
var s3 = new AWS.S3();
var params = {Bucket: 'aws4test', Key: 'excalibur/items/stark.jpg', Expires: 300};
var url = s3.getSignedUrl('putObject', params);
fs.readFile('./stark.jpg', function(err, data){
if(err){
return console.log(err);
}
req({
method: "PUT",
url: url,
body: data
}, function(err, res, body){
console.log(body);
})
});
Also, @chrisradek I just tried it like you said and sure enough that all worked too ... which I like better. So thanks again!!
@chrisradek Thank you. The signatureVersion: 'v4' in the AWS config is what let the PUT succeed.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.
Most helpful comment
@raffi-minassian
There are two changes you'll need to make so that you can use streams and request to upload a file using a presigned url.
First,
requestis using chunked uploads when it is given a stream as the body to upload. Signature Version 4 needs to be used when generating the presigned url to support this. When instantiating your s3 client, pass in thesignatureVersion: 'v4'parameter:requestalso automatically sets theTransfer-Encodingheader when using streams, but S3 needs to know what the final content-length is. In the options you pass into request, you'll need to supply theContent-Lengthheader.There's some additional information here:
http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
Let me know if this helps!