I am attempting to create a signed S3 URL using Javascript & NodeJS. I have used this specification.
var crypto = require(\'crypto\'),
date =
My implementation using AWS-SDK and Rx.
import AWS from "aws-sdk"
import Rx from 'rx'
/*
* Credentials could be loaded from env variables
* http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html
* */
const s3 = new AWS.S3({apiVersion: '2006-03-01'});
export function getS3SignedImage(objectKey) {
return Rx.Observable.create(function (observer) {
s3.getSignedUrl('getObject',{
Bucket: process.env.AWS_BUCKET,
Key: objectKey
}, (err, data) => {
if (err) {
return observer.onError(err);
}
observer.onNext(data);
observer.onCompleted();
});
});
}
maybe one too many newlines?
var stringToSign ='GET\n\n\n' + date + '\n\n' + resource;
If its any help here is a rubbish PHP implementation which definitely works:
class myS3Helper{
public function getSignedImageLink($timeout = 1800)
{
$now = new Zend_Date(); //Gives us a time object that is set to NOW
$now->setTimezone('UTC'); //Set to UTC a-la AWS requirements
$now->addSecond($timeout);
$expirationTime = $now->getTimestamp(); //returns unix timestamp representation of the time.
$signature = urlencode(
base64_encode(
hash_hmac(
'sha1', $this->_generateStringToSign($expirationTime),
$my_aws_secretkey,
true
)
)
);
//FIXME make this less ugly when I know it works
$url = 'https://';
$url .= Zend_Service_Amazon_S3::S3_ENDPOINT; //e.g s3.amazonaws.com
$url .= $this->_getImagePath(); //e.g /mybucket/myFirstCar.jpg
$url .='?AWSAccessKeyId=' . $my_aws_key;
$url .='&Signature=' . $signature; //signature as returned by below function
$url .='&Expires=' . $expirationTime;
return $url;
}
protected function _generateStringToSign($expires)
{
$string = "GET\n"; //Methods
$string .= "\n";
$string .= "\n";
$string .= "$expires\n"; //Expires
$string .= $this->_getImagePath();
return $string;
}
}
EDIT--
Have a look at this node.js s3 upload code, (it's not mine but found it lying around on my mac - so if anyone can attribute it to someone let me know and i'll do the props). Hopefully this might help (3rd time lucky)
https://gist.github.com/1370593
I would try using Knox along with Node.Js . Its known to be a great combination and also itself utilizes the Node.JS Crypto library which is kind of what you're trying to do - saving you time:)
More info here : https://github.com/LearnBoost/knox
Than, you could just do something like:
var knox = require('knox');
var s3Client = knox.createClient({
key: 'XXX',
secret: 'XXX',
bucket: 'XXX'
});
var expires = new Date();
expires.setMinutes(expires.getMinutes() + 30);
var url = s3Client.signedUrl(filename, expires);
Edit:
You could also look into Knox and just check what the signedUrl function does and implement that yourself.Than you could add to the auth.signQuery
call an extra option called amazonHeaders
:
Client.prototype.signedUrl = function(filename, expiration){
var epoch = Math.floor(expiration.getTime()/1000);
var signature = auth.signQuery({
amazonHeaders: 'response-content-disposition:attachment',
secret: this.secret,
date: epoch,
resource: '/' + this.bucket + url.parse(filename).pathname
});
return this.url(filename) +
'?Expires=' + epoch +
'&AWSAccessKeyId=' + this.key +
'&Signature=' + encodeURIComponent(signature);
};
Shai.