• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..20-Sep-2020-

.travis.ymlH A D26-Oct-198559 65

LICENSEH A D26-Oct-19851.1 KiB2016

README.mdH A D26-Oct-198516.4 KiB524442

package.jsonH A D20-Sep-20202.2 KiB104103

README.md

1aws4
2----
3
4[![Build Status](https://secure.travis-ci.org/mhart/aws4.png?branch=master)](http://travis-ci.org/mhart/aws4)
5
6A small utility to sign vanilla node.js http(s) request options using Amazon's
7[AWS Signature Version 4](http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html).
8
9Can also be used [in the browser](./browser).
10
11This signature is supported by nearly all Amazon services, including
12[S3](http://docs.aws.amazon.com/AmazonS3/latest/API/),
13[EC2](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/),
14[DynamoDB](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API.html),
15[Kinesis](http://docs.aws.amazon.com/kinesis/latest/APIReference/),
16[Lambda](http://docs.aws.amazon.com/lambda/latest/dg/API_Reference.html),
17[SQS](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/),
18[SNS](http://docs.aws.amazon.com/sns/latest/api/),
19[IAM](http://docs.aws.amazon.com/IAM/latest/APIReference/),
20[STS](http://docs.aws.amazon.com/STS/latest/APIReference/),
21[RDS](http://docs.aws.amazon.com/AmazonRDS/latest/APIReference/),
22[CloudWatch](http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/),
23[CloudWatch Logs](http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/),
24[CodeDeploy](http://docs.aws.amazon.com/codedeploy/latest/APIReference/),
25[CloudFront](http://docs.aws.amazon.com/AmazonCloudFront/latest/APIReference/),
26[CloudTrail](http://docs.aws.amazon.com/awscloudtrail/latest/APIReference/),
27[ElastiCache](http://docs.aws.amazon.com/AmazonElastiCache/latest/APIReference/),
28[EMR](http://docs.aws.amazon.com/ElasticMapReduce/latest/API/),
29[Glacier](http://docs.aws.amazon.com/amazonglacier/latest/dev/amazon-glacier-api.html),
30[CloudSearch](http://docs.aws.amazon.com/cloudsearch/latest/developerguide/APIReq.html),
31[Elastic Load Balancing](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/APIReference/),
32[Elastic Transcoder](http://docs.aws.amazon.com/elastictranscoder/latest/developerguide/api-reference.html),
33[CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/),
34[Elastic Beanstalk](http://docs.aws.amazon.com/elasticbeanstalk/latest/api/),
35[Storage Gateway](http://docs.aws.amazon.com/storagegateway/latest/userguide/AWSStorageGatewayAPI.html),
36[Data Pipeline](http://docs.aws.amazon.com/datapipeline/latest/APIReference/),
37[Direct Connect](http://docs.aws.amazon.com/directconnect/latest/APIReference/),
38[Redshift](http://docs.aws.amazon.com/redshift/latest/APIReference/),
39[OpsWorks](http://docs.aws.amazon.com/opsworks/latest/APIReference/),
40[SES](http://docs.aws.amazon.com/ses/latest/APIReference/),
41[SWF](http://docs.aws.amazon.com/amazonswf/latest/apireference/),
42[AutoScaling](http://docs.aws.amazon.com/AutoScaling/latest/APIReference/),
43[Mobile Analytics](http://docs.aws.amazon.com/mobileanalytics/latest/ug/server-reference.html),
44[Cognito Identity](http://docs.aws.amazon.com/cognitoidentity/latest/APIReference/),
45[Cognito Sync](http://docs.aws.amazon.com/cognitosync/latest/APIReference/),
46[Container Service](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/),
47[AppStream](http://docs.aws.amazon.com/appstream/latest/developerguide/appstream-api-rest.html),
48[Key Management Service](http://docs.aws.amazon.com/kms/latest/APIReference/),
49[Config](http://docs.aws.amazon.com/config/latest/APIReference/),
50[CloudHSM](http://docs.aws.amazon.com/cloudhsm/latest/dg/api-ref.html),
51[Route53](http://docs.aws.amazon.com/Route53/latest/APIReference/requests-rest.html) and
52[Route53 Domains](http://docs.aws.amazon.com/Route53/latest/APIReference/requests-rpc.html).
53
54Indeed, the only AWS services that *don't* support v4 as of 2014-12-30 are
55[Import/Export](http://docs.aws.amazon.com/AWSImportExport/latest/DG/api-reference.html) and
56[SimpleDB](http://docs.aws.amazon.com/AmazonSimpleDB/latest/DeveloperGuide/SDB_API.html)
57(they only support [AWS Signature Version 2](https://github.com/mhart/aws2)).
58
59It also provides defaults for a number of core AWS headers and
60request parameters, making it very easy to query AWS services, or
61build out a fully-featured AWS library.
62
63Example
64-------
65
66```javascript
67var http  = require('http'),
68    https = require('https'),
69    aws4  = require('aws4')
70
71// given an options object you could pass to http.request
72var opts = {host: 'sqs.us-east-1.amazonaws.com', path: '/?Action=ListQueues'}
73
74// alternatively (as aws4 can infer the host):
75opts = {service: 'sqs', region: 'us-east-1', path: '/?Action=ListQueues'}
76
77// alternatively (as us-east-1 is default):
78opts = {service: 'sqs', path: '/?Action=ListQueues'}
79
80aws4.sign(opts) // assumes AWS credentials are available in process.env
81
82console.log(opts)
83/*
84{
85  host: 'sqs.us-east-1.amazonaws.com',
86  path: '/?Action=ListQueues',
87  headers: {
88    Host: 'sqs.us-east-1.amazonaws.com',
89    'X-Amz-Date': '20121226T061030Z',
90    Authorization: 'AWS4-HMAC-SHA256 Credential=ABCDEF/20121226/us-east-1/sqs/aws4_request, ...'
91  }
92}
93*/
94
95// we can now use this to query AWS using the standard node.js http API
96http.request(opts, function(res) { res.pipe(process.stdout) }).end()
97/*
98<?xml version="1.0"?>
99<ListQueuesResponse xmlns="http://queue.amazonaws.com/doc/2012-11-05/">
100...
101*/
102```
103
104More options
105------------
106
107```javascript
108// you can also pass AWS credentials in explicitly (otherwise taken from process.env)
109aws4.sign(opts, {accessKeyId: '', secretAccessKey: ''})
110
111// can also add the signature to query strings
112aws4.sign({service: 's3', path: '/my-bucket?X-Amz-Expires=12345', signQuery: true})
113
114// create a utility function to pipe to stdout (with https this time)
115function request(o) { https.request(o, function(res) { res.pipe(process.stdout) }).end(o.body || '') }
116
117// aws4 can infer the HTTP method if a body is passed in
118// method will be POST and Content-Type: 'application/x-www-form-urlencoded; charset=utf-8'
119request(aws4.sign({service: 'iam', body: 'Action=ListGroups&Version=2010-05-08'}))
120/*
121<ListGroupsResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
122...
123*/
124
125// can specify any custom option or header as per usual
126request(aws4.sign({
127  service: 'dynamodb',
128  region: 'ap-southeast-2',
129  method: 'POST',
130  path: '/',
131  headers: {
132    'Content-Type': 'application/x-amz-json-1.0',
133    'X-Amz-Target': 'DynamoDB_20120810.ListTables'
134  },
135  body: '{}'
136}))
137/*
138{"TableNames":[]}
139...
140*/
141
142// works with all other services that support Signature Version 4
143
144request(aws4.sign({service: 's3', path: '/', signQuery: true}))
145/*
146<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
147...
148*/
149
150request(aws4.sign({service: 'ec2', path: '/?Action=DescribeRegions&Version=2014-06-15'}))
151/*
152<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
153...
154*/
155
156request(aws4.sign({service: 'sns', path: '/?Action=ListTopics&Version=2010-03-31'}))
157/*
158<ListTopicsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
159...
160*/
161
162request(aws4.sign({service: 'sts', path: '/?Action=GetSessionToken&Version=2011-06-15'}))
163/*
164<GetSessionTokenResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
165...
166*/
167
168request(aws4.sign({service: 'cloudsearch', path: '/?Action=ListDomainNames&Version=2013-01-01'}))
169/*
170<ListDomainNamesResponse xmlns="http://cloudsearch.amazonaws.com/doc/2013-01-01/">
171...
172*/
173
174request(aws4.sign({service: 'ses', path: '/?Action=ListIdentities&Version=2010-12-01'}))
175/*
176<ListIdentitiesResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
177...
178*/
179
180request(aws4.sign({service: 'autoscaling', path: '/?Action=DescribeAutoScalingInstances&Version=2011-01-01'}))
181/*
182<DescribeAutoScalingInstancesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
183...
184*/
185
186request(aws4.sign({service: 'elasticloadbalancing', path: '/?Action=DescribeLoadBalancers&Version=2012-06-01'}))
187/*
188<DescribeLoadBalancersResponse xmlns="http://elasticloadbalancing.amazonaws.com/doc/2012-06-01/">
189...
190*/
191
192request(aws4.sign({service: 'cloudformation', path: '/?Action=ListStacks&Version=2010-05-15'}))
193/*
194<ListStacksResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
195...
196*/
197
198request(aws4.sign({service: 'elasticbeanstalk', path: '/?Action=ListAvailableSolutionStacks&Version=2010-12-01'}))
199/*
200<ListAvailableSolutionStacksResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
201...
202*/
203
204request(aws4.sign({service: 'rds', path: '/?Action=DescribeDBInstances&Version=2012-09-17'}))
205/*
206<DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/doc/2012-09-17/">
207...
208*/
209
210request(aws4.sign({service: 'monitoring', path: '/?Action=ListMetrics&Version=2010-08-01'}))
211/*
212<ListMetricsResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
213...
214*/
215
216request(aws4.sign({service: 'redshift', path: '/?Action=DescribeClusters&Version=2012-12-01'}))
217/*
218<DescribeClustersResponse xmlns="http://redshift.amazonaws.com/doc/2012-12-01/">
219...
220*/
221
222request(aws4.sign({service: 'cloudfront', path: '/2014-05-31/distribution'}))
223/*
224<DistributionList xmlns="http://cloudfront.amazonaws.com/doc/2014-05-31/">
225...
226*/
227
228request(aws4.sign({service: 'elasticache', path: '/?Action=DescribeCacheClusters&Version=2014-07-15'}))
229/*
230<DescribeCacheClustersResponse xmlns="http://elasticache.amazonaws.com/doc/2014-07-15/">
231...
232*/
233
234request(aws4.sign({service: 'elasticmapreduce', path: '/?Action=DescribeJobFlows&Version=2009-03-31'}))
235/*
236<DescribeJobFlowsResponse xmlns="http://elasticmapreduce.amazonaws.com/doc/2009-03-31">
237...
238*/
239
240request(aws4.sign({service: 'route53', path: '/2013-04-01/hostedzone'}))
241/*
242<ListHostedZonesResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
243...
244*/
245
246request(aws4.sign({service: 'appstream', path: '/applications'}))
247/*
248{"_links":{"curie":[{"href":"http://docs.aws.amazon.com/appstream/latest/...
249...
250*/
251
252request(aws4.sign({service: 'cognito-sync', path: '/identitypools'}))
253/*
254{"Count":0,"IdentityPoolUsages":[],"MaxResults":16,"NextToken":null}
255...
256*/
257
258request(aws4.sign({service: 'elastictranscoder', path: '/2012-09-25/pipelines'}))
259/*
260{"NextPageToken":null,"Pipelines":[]}
261...
262*/
263
264request(aws4.sign({service: 'lambda', path: '/2014-11-13/functions/'}))
265/*
266{"Functions":[],"NextMarker":null}
267...
268*/
269
270request(aws4.sign({service: 'ecs', path: '/?Action=ListClusters&Version=2014-11-13'}))
271/*
272<ListClustersResponse xmlns="http://ecs.amazonaws.com/doc/2014-11-13/">
273...
274*/
275
276request(aws4.sign({service: 'glacier', path: '/-/vaults', headers: {'X-Amz-Glacier-Version': '2012-06-01'}}))
277/*
278{"Marker":null,"VaultList":[]}
279...
280*/
281
282request(aws4.sign({service: 'storagegateway', body: '{}', headers: {
283  'Content-Type': 'application/x-amz-json-1.1',
284  'X-Amz-Target': 'StorageGateway_20120630.ListGateways'
285}}))
286/*
287{"Gateways":[]}
288...
289*/
290
291request(aws4.sign({service: 'datapipeline', body: '{}', headers: {
292  'Content-Type': 'application/x-amz-json-1.1',
293  'X-Amz-Target': 'DataPipeline.ListPipelines'
294}}))
295/*
296{"hasMoreResults":false,"pipelineIdList":[]}
297...
298*/
299
300request(aws4.sign({service: 'opsworks', body: '{}', headers: {
301  'Content-Type': 'application/x-amz-json-1.1',
302  'X-Amz-Target': 'OpsWorks_20130218.DescribeStacks'
303}}))
304/*
305{"Stacks":[]}
306...
307*/
308
309request(aws4.sign({service: 'route53domains', body: '{}', headers: {
310  'Content-Type': 'application/x-amz-json-1.1',
311  'X-Amz-Target': 'Route53Domains_v20140515.ListDomains'
312}}))
313/*
314{"Domains":[]}
315...
316*/
317
318request(aws4.sign({service: 'kinesis', body: '{}', headers: {
319  'Content-Type': 'application/x-amz-json-1.1',
320  'X-Amz-Target': 'Kinesis_20131202.ListStreams'
321}}))
322/*
323{"HasMoreStreams":false,"StreamNames":[]}
324...
325*/
326
327request(aws4.sign({service: 'cloudtrail', body: '{}', headers: {
328  'Content-Type': 'application/x-amz-json-1.1',
329  'X-Amz-Target': 'CloudTrail_20131101.DescribeTrails'
330}}))
331/*
332{"trailList":[]}
333...
334*/
335
336request(aws4.sign({service: 'logs', body: '{}', headers: {
337  'Content-Type': 'application/x-amz-json-1.1',
338  'X-Amz-Target': 'Logs_20140328.DescribeLogGroups'
339}}))
340/*
341{"logGroups":[]}
342...
343*/
344
345request(aws4.sign({service: 'codedeploy', body: '{}', headers: {
346  'Content-Type': 'application/x-amz-json-1.1',
347  'X-Amz-Target': 'CodeDeploy_20141006.ListApplications'
348}}))
349/*
350{"applications":[]}
351...
352*/
353
354request(aws4.sign({service: 'directconnect', body: '{}', headers: {
355  'Content-Type': 'application/x-amz-json-1.1',
356  'X-Amz-Target': 'OvertureService.DescribeConnections'
357}}))
358/*
359{"connections":[]}
360...
361*/
362
363request(aws4.sign({service: 'kms', body: '{}', headers: {
364  'Content-Type': 'application/x-amz-json-1.1',
365  'X-Amz-Target': 'TrentService.ListKeys'
366}}))
367/*
368{"Keys":[],"Truncated":false}
369...
370*/
371
372request(aws4.sign({service: 'config', body: '{}', headers: {
373  'Content-Type': 'application/x-amz-json-1.1',
374  'X-Amz-Target': 'StarlingDoveService.DescribeDeliveryChannels'
375}}))
376/*
377{"DeliveryChannels":[]}
378...
379*/
380
381request(aws4.sign({service: 'cloudhsm', body: '{}', headers: {
382  'Content-Type': 'application/x-amz-json-1.1',
383  'X-Amz-Target': 'CloudHsmFrontendService.ListAvailableZones'
384}}))
385/*
386{"AZList":["us-east-1a","us-east-1b","us-east-1c"]}
387...
388*/
389
390request(aws4.sign({
391  service: 'swf',
392  body: '{"registrationStatus":"REGISTERED"}',
393  headers: {
394    'Content-Type': 'application/x-amz-json-1.0',
395    'X-Amz-Target': 'SimpleWorkflowService.ListDomains'
396  }
397}))
398/*
399{"domainInfos":[]}
400...
401*/
402
403request(aws4.sign({
404  service: 'cognito-identity',
405  body: '{"MaxResults": 1}',
406  headers: {
407    'Content-Type': 'application/x-amz-json-1.1',
408    'X-Amz-Target': 'AWSCognitoIdentityService.ListIdentityPools'
409  }
410}))
411/*
412{"IdentityPools":[]}
413...
414*/
415
416request(aws4.sign({
417  service: 'mobileanalytics',
418  path: '/2014-06-05/events',
419  body: JSON.stringify({events:[{
420    eventType: 'a',
421    timestamp: new Date().toISOString(),
422    session: {},
423  }]}),
424  headers: {
425    'Content-Type': 'application/json',
426    'X-Amz-Client-Context': JSON.stringify({
427      client: {client_id: 'a', app_title: 'a'},
428      custom: {},
429      env: {platform: 'a'},
430      services: {},
431    }),
432  }
433}))
434/*
435(HTTP 202, empty response)
436*/
437
438// Generate CodeCommit Git access password
439var signer = new aws4.RequestSigner({
440  service: 'codecommit',
441  host: 'git-codecommit.us-east-1.amazonaws.com',
442  method: 'GIT',
443  path: '/v1/repos/MyAwesomeRepo',
444})
445var password = signer.getDateTime() + 'Z' + signer.signature()
446```
447
448API
449---
450
451### aws4.sign(requestOptions, [credentials])
452
453This calculates and populates the `Authorization` header of
454`requestOptions`, and any other necessary AWS headers and/or request
455options. Returns `requestOptions` as a convenience for chaining.
456
457`requestOptions` is an object holding the same options that the node.js
458[http.request](http://nodejs.org/docs/latest/api/http.html#http_http_request_options_callback)
459function takes.
460
461The following properties of `requestOptions` are used in the signing or
462populated if they don't already exist:
463
464- `hostname` or `host` (will be determined from `service` and `region` if not given)
465- `method` (will use `'GET'` if not given or `'POST'` if there is a `body`)
466- `path` (will use `'/'` if not given)
467- `body` (will use `''` if not given)
468- `service` (will be calculated from `hostname` or `host` if not given)
469- `region` (will be calculated from `hostname` or `host` or use `'us-east-1'` if not given)
470- `headers['Host']` (will use `hostname` or `host` or be calculated if not given)
471- `headers['Content-Type']` (will use `'application/x-www-form-urlencoded; charset=utf-8'`
472  if not given and there is a `body`)
473- `headers['Date']` (used to calculate the signature date if given, otherwise `new Date` is used)
474
475Your AWS credentials (which can be found in your
476[AWS console](https://portal.aws.amazon.com/gp/aws/securityCredentials))
477can be specified in one of two ways:
478
479- As the second argument, like this:
480
481```javascript
482aws4.sign(requestOptions, {
483  secretAccessKey: "<your-secret-access-key>",
484  accessKeyId: "<your-access-key-id>",
485  sessionToken: "<your-session-token>"
486})
487```
488
489- From `process.env`, such as this:
490
491```
492export AWS_SECRET_ACCESS_KEY="<your-secret-access-key>"
493export AWS_ACCESS_KEY_ID="<your-access-key-id>"
494export AWS_SESSION_TOKEN="<your-session-token>"
495```
496
497(will also use `AWS_ACCESS_KEY` and `AWS_SECRET_KEY` if available)
498
499The `sessionToken` property and `AWS_SESSION_TOKEN` environment variable are optional for signing
500with [IAM STS temporary credentials](http://docs.aws.amazon.com/STS/latest/UsingSTS/using-temp-creds.html).
501
502Installation
503------------
504
505With [npm](http://npmjs.org/) do:
506
507```
508npm install aws4
509```
510
511Can also be used [in the browser](./browser).
512
513Thanks
514------
515
516Thanks to [@jed](https://github.com/jed) for his
517[dynamo-client](https://github.com/jed/dynamo-client) lib where I first
518committed and subsequently extracted this code.
519
520Also thanks to the
521[official node.js AWS SDK](https://github.com/aws/aws-sdk-js) for giving
522me a start on implementing the v4 signature.
523
524