Gabriele Cristoni Gabriele Cristoni - 5 months ago 197
AngularJS Question

Heroku NodeJS: Error SignatureDoesNotMatch with aws-sdk

Node: 5.6.0
Angular: 1.4.5
Sails: 0.12.1

I'm building an app using SailsJS and Angular, I need to upload an image to S3, but I keep on getting the SignatureDoesNotMatch error when running the PUT http request.

I followed this heroku guide but obviously I had to use angular and sails rather than vanilla js and express.

I've searched around a lot but I couldn't find anything that really helped.
Example: https://github.com/aws/aws-sdk-js/issues/86

I've no clue on what I am doing wrong.

Here is the code:
AwsController:

var aws = require('aws-sdk');

module.exports = {
"sign-s3": function(req, res) {
const s3 = new aws.S3({region: 'eu-west-1'});
const fileName = req.param('file-name');
const fileType = req.param('file-type');
const s3Params = {
Bucket: process.env.S3_BUCKET,
Key: fileName,
Expires: 60,
ContentType: fileType,
ACL: 'public-read'
};

s3.getSignedUrl('putObject', s3Params, function (err, data) {
if(err){
console.log(err);
return res.end();
}

const returnData = {
signedRequest: data,
url: 'https://' + process.env.S3_BUCKET + '.s3.amazonaws.com/' + fileName
};

return res.json(returnData);
});
}
};


app.factory.aws-manager.js:

function getSignedRequest(file) {
return $http.get('/aws/sign-s3?file-name=' + file.name + '&file-type=' + file.type)
.then(successCallback)
.catch(errorCallback);

function successCallback(response) {
return response.data;
}

function errorCallback(response) {
console.log(response);
return response;
}
}

function sendFile(file, signedRequest, url){
var data = {file: file};

return $http.put(signedRequest, data)
.then(successCallback)
.catch(errorCallback);

function successCallback(response) {
return { data: response.data, url: url};
}

function errorCallback(response) {
console.log(response);
return response;
}
}

function uploadFile(file) {
return getSignedRequest(file)
.then(successCallback);

function successCallback(data) {
console.log(data);
return sendFile(file, data.signedRequest, data.url)
.then(successCallback);

function successCallback(response) {
return response.url;
}
}
}


The call:

awsManager.uploadFile(file);


The error:

<?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>AKIAJFI5FDWJK7TTGIKQ</AWSAccessKeyId>
<StringToSign>PUT application/json;charset=UTF-8 1465728343 x-amz-acl:public-read /aemporium-assets/einstein.jpg</StringToSign>
<SignatureProvided>mtVl69xyqhAbYhQt1oZk0F1w1WE=</SignatureProvided>
<StringToSignBytes>50 55 54 0a 0a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f 6e 3b 63 68 61 72 73 65 74 3d 55 54 46 2d 38 0a 31 34 36 35 37 32 38 33 34 33 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 2f 61 65 6d 70 6f 72 69 75 6d 2d 61 73 73 65 74 73 2f 65 69 6e 73 74 65 69 6e 2e 6a 70 67</StringToSignBytes>
<RequestId>900A623A516D2DDD</RequestId>
<HostId>j1t+w1N6MLeWA2UAirQbTXIa2+U5vVtMTSYiRo72SzOnzDf0TyDZxlL4VoXnCLqXu+hv/rxN9Z8=</HostId>
</Error>


(I'm sure all the process.env.* variables are correct)

Answer

I found the problem, i didn't notice that in the request headers in the PUT call i had 2 wrong parameters:

  1. Accept: was json/application, it had to be */*

  2. Content-Type: it was json/application, it had to be "file.type"

The corrected code:

function sendFile(file, signedRequest, url){
  var data = {file: file};

  return $http.put(signedRequest, file, {"headers": {"Accept":"*/*", "Content-Type":file.type}})
    .then(successCallback)
    .catch(errorCallback);

  function successCallback(response) {
    return { data: response.data, url: url};
  }

  function errorCallback(response) {
    console.log(response);
    return response;
  }
}