Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Amazon S3 POST api, and signing a policy with NodeJS

Tags:

I'm trying to get an built that allows users to upload a file directly to my Amazon S3 bucket, from a NodeJS powered website. It seems the only tutorials out there, other than the actual amazon docs for this are all very out of date.

I've been following this tutorial, for the basic info, but again it's out dated. It doesn't have the method calls to crypto correct, as it tries to pass a raw JavaScript object to the update method, which throws an error because it's not a string or buffer.

I've also been looking at the source for the knox npm package. It doesn't have POST support built in - which I totally understand, because it's the browser doing the POST once it has the right fields. Knox does appear to have the right code to sign a policy, and I've tried to get my code working based on this... but again to no avail.

Here is what I've come up with, for code. It produces a base64 encoded policy, and it creates a signature... but it's the wrong signature according to Amazon, when I try to do a file upload.

 var crypto = require("crypto"); var config = require("../../amazonConfig.json");  exports.createS3Policy = function(callback) {   var date = new Date();    var s3Policy = {     "expiration": "2014-12-01T12:00:00.000Z",     "conditions": [       {"acl": "public-read"},        ["content-length-range", 0, 2147483648],       {"bucket": "signalleaf"},        ["starts-with", "$Cache-Control", ""],       ["starts-with", "$Content-Type", ""],       ["starts-with", "$Content-Disposition", ""],       ["starts-with", "$Content-Encoding", ""],       ["starts-with", "$Expires", ""],       ["starts-with", "$key", "/myfolder/"],        {"success_action_redirect": "http://example.com/uploadsuccess"},     ]   };    var stringPolicy = JSON.stringify(s3Policy).toString("utf-8");   var buffer = Buffer(stringPolicy, "utf-8");    var encoded = buffer.toString("base64");   var signature = crypto.createHmac("sha1", config.secretKey)     .update(new Buffer(stringPolicy, "utf-8")).digest("base64");     var s3Credentials = {     s3PolicyBase64: encoded,     s3Signature: signature   };    GLOBAL.s3creds = s3Credentials;    callback(s3Credentials); };  

I'm obviously doing something wrong, here. But I have no idea what. Can anyone help identify what I'm doing wrong? Where my problem is? Does anyone have a working tutorial for how to generate a proper Amazon S3 Policy, with signature, from NodeJS v0.10.x, for a POST to the s3 REST api?

like image 700
Derick Bailey Avatar asked Aug 27 '13 22:08

Derick Bailey


1 Answers

Ok, I finally figured it out. After playing the random guessing game for a VERY long time, I thought to myself

"maybe i need to sign the base64 encoded policy" - me

and BAM that was it.

I also re-ordered the conditions to match how the form is posting, though I'm not sure this makes a difference.

var crypto = require("crypto"); var config = require("../../amazonConfig.json");  exports.createS3Policy = function(contentType, callback) {   var date = new Date();    var s3Policy = {     "expiration": "2014-12-01T12:00:00.000Z", // hard coded for testing     "conditions": [       ["starts-with", "$key", "somefolder/"],        {"bucket": "my-bucket-name"},        {"acl": "public-read"},        ["starts-with", "$Content-Type", contentType],       {"success_action_redirect": "http://example.com/uploadsuccess"},     ]   };    // stringify and encode the policy   var stringPolicy = JSON.stringify(s3Policy);   var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64");    // sign the base64 encoded policy   var signature = crypto.createHmac("sha1", config.secretKey)     .update(new Buffer(base64Policy, "utf-8")).digest("base64");    // build the results object   var s3Credentials = {     s3Policy: base64Policy,     s3Signature: signature   };    // send it back   callback(s3Credentials); }; 

Hopefully this will help others that run in to the same problem.

like image 76
Derick Bailey Avatar answered Oct 05 '22 19:10

Derick Bailey