@@ -2,6 +2,7 @@ import { sha256Hex } from "./deps.ts";
22import { toAmz , toDateStamp } from "./src/date.ts" ;
33import { getSignatureKey , signAwsV4 } from "./src/signing.ts" ;
44import type { Credentials , RequestHeaders } from "./src/types.ts" ;
5+ import { equal } from "https://deno.land/std@0.68.0/testing/asserts.ts" ;
56export type { Credentials , RequestHeaders } ;
67
78/**
@@ -15,17 +16,14 @@ export type { Credentials, RequestHeaders };
1516 *
1617 * ```ts
1718 * const signer = new AWSSignerV4();
18- * const url = "https://test-bucket.s3.amazonaws.com/test";
19- * const method = "PUT";
2019 * const body = new TextEncoder().encode("Hello World!")
21- * const headers = { "content-length": body.length };
22- * const signedHeaders = signer.sign("s3", url, method, headers, body);
23- *
24- * const response = await fetch(url, {
25- * headers: signedHeaders,
26- * method,
27- * body: payload,
20+ * const request = new Request("https://test-bucket.s3.amazonaws.com/test", {
21+ * method: "PUT",
22+ * headers: { "content-length": body.length.toString() },
23+ * body,
2824 * });
25+ * const req = await signer.sign("s3", request);
26+ * const response = await fetch(req);
2927 * ```
3028 */
3129export class AWSSignerV4 {
@@ -44,6 +42,7 @@ export class AWSSignerV4 {
4442 this . region = region || this . #getDefaultRegion( ) ;
4543 this . credentials = credentials || this . #getDefaultCredentials( ) ;
4644 }
45+
4746 /**
4847 * Use this to create the signed headers required to
4948 * make a call to an AWS API.
@@ -55,42 +54,42 @@ export class AWSSignerV4 {
5554 * @param body The body for PUT/POST methods.
5655 * @returns {RequestHeaders } - the signed request headers
5756 */
58- public sign = (
57+ public async sign (
5958 service : string ,
60- url : string ,
61- method : string = "GET" ,
62- headers : RequestHeaders ,
63- body ?: Uint8Array | string ,
64- ) : RequestHeaders => {
59+ request : Request ,
60+ ) : Promise < Request > {
6561 const date = new Date ( ) ;
6662 const amzdate = toAmz ( date ) ;
6763 const datestamp = toDateStamp ( date ) ;
6864
69- const urlObj = new URL ( url ) ;
65+ const urlObj = new URL ( request . url ) ;
7066 const { host, pathname, searchParams } = urlObj ;
7167 const canonicalQuerystring = searchParams . toString ( ) ;
7268
73- headers [ "x-amz-date" ] = amzdate ;
69+ const headers = new Headers ( request . headers ) ;
70+
71+ headers . set ( "x-amz-date" , amzdate ) ;
7472 if ( this . credentials . sessionToken ) {
75- headers [ "x-amz-security-token" ] = this . credentials . sessionToken ;
73+ headers . set ( "x-amz-security-token" , this . credentials . sessionToken ) ;
7674 }
77-
78- headers [ "host" ] = host ;
75+ headers . set ( "host" , host ) ;
7976
8077 let canonicalHeaders = "" ;
8178 let signedHeaders = "" ;
82- for ( const key of Object . keys ( headers ) . sort ( ) ) {
83- canonicalHeaders += `${ key . toLowerCase ( ) } :${ headers [ key ] } \n` ;
79+ for ( const key of [ ... headers . keys ( ) ] . sort ( ) ) {
80+ canonicalHeaders += `${ key . toLowerCase ( ) } :${ headers . get ( key ) } \n` ;
8481 signedHeaders += `${ key . toLowerCase ( ) } ;` ;
8582 }
8683 signedHeaders = signedHeaders . substring ( 0 , signedHeaders . length - 1 ) ;
87- const payload = body ?? "" ;
88- const payloadHash = sha256Hex ( payload ) ;
84+ const body = request . body
85+ ? new Uint8Array ( await request . arrayBuffer ( ) )
86+ : new Uint8Array ( ) ;
87+ const payloadHash = sha256Hex ( body ) ;
8988
9089 const { awsAccessKeyId, awsSecretKey } = this . credentials ;
9190
9291 const canonicalRequest =
93- `${ method } \n${ pathname } \n${ canonicalQuerystring } \n${ canonicalHeaders } \n${ signedHeaders } \n${ payloadHash } ` ;
92+ `${ request . method } \n${ pathname } \n${ canonicalQuerystring } \n${ canonicalHeaders } \n${ signedHeaders } \n${ payloadHash } ` ;
9493 const canonicalRequestDigest = sha256Hex ( canonicalRequest ) ;
9594
9695 const algorithm = "AWS4-HMAC-SHA256" ;
@@ -111,10 +110,27 @@ export class AWSSignerV4 {
111110 const authHeader =
112111 `${ algorithm } Credential=${ awsAccessKeyId } /${ credentialScope } , SignedHeaders=${ signedHeaders } , Signature=${ signature } ` ;
113112
114- headers . Authorization = authHeader ;
115-
116- return headers ;
117- } ;
113+ headers . set ( "Authorization" , authHeader ) ;
114+
115+ const req = new Request (
116+ request . url ,
117+ {
118+ headers,
119+ method : request . method ,
120+ body,
121+ cache : request . cache ,
122+ credentials : request . credentials ,
123+ integrity : request . integrity ,
124+ keepalive : request . keepalive ,
125+ mode : request . mode ,
126+ redirect : request . redirect ,
127+ referrer : request . referrer ,
128+ referrerPolicy : request . referrerPolicy ,
129+ signal : request . signal ,
130+ } ,
131+ ) ;
132+ return req ;
133+ }
118134
119135 #getDefaultCredentials = ( ) : Credentials => {
120136 const AWS_ACCESS_KEY_ID = Deno . env . get ( "AWS_ACCESS_KEY_ID" ) ;
0 commit comments