Skip to content

Commit 8d6b27a

Browse files
ronak2121trivikr
andcommitted
feat(signature-v4): add support to override the set of unsignableHeaders (#420)
Co-Authored-By: Trivikram Kamat <[email protected]>
1 parent ae71c3e commit 8d6b27a

File tree

5 files changed

+88
-8
lines changed

5 files changed

+88
-8
lines changed

Diff for: packages/signature-v4/src/SignatureV4.spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,27 @@ describe("SignatureV4", () => {
452452
);
453453
});
454454

455+
it("should allow specifying custom signable headers to override custom and always unsignable ones", async () => {
456+
const { headers } = await signer.sign(
457+
{
458+
...minimalRequest,
459+
headers: {
460+
host: "foo.us-bar-1.amazonaws.com",
461+
foo: "bar",
462+
"user-agent": "baz"
463+
}
464+
},
465+
{
466+
signingDate: new Date("2000-01-01T00:00:00.000Z"),
467+
unsignableHeaders: new Set(["foo"]),
468+
signableHeaders: new Set(["foo", "user-agent"])
469+
}
470+
);
471+
expect(headers[AUTH_HEADER]).toMatch(
472+
/^AWS4-HMAC-SHA256 Credential=foo\/20000101\/us-bar-1\/foo\/aws4_request, SignedHeaders=foo;host;user-agent;x-amz-content-sha256;x-amz-date, Signature=/
473+
);
474+
});
475+
455476
it("should support signing with asynchronously resolved credentials", async () => {
456477
const credsProvider = () =>
457478
Promise.resolve({

Diff for: packages/signature-v4/src/SignatureV4.ts

+23-6
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,11 @@ export class SignatureV4
133133
this.credentialProvider()
134134
]);
135135

136-
const { signingDate = new Date(), unsignableHeaders } = options;
136+
const {
137+
signingDate = new Date(),
138+
unsignableHeaders,
139+
signableHeaders
140+
} = options;
137141

138142
const { longDate, shortDate } = formatDate(signingDate);
139143
const ttl = getTtl(signingDate, expiration);
@@ -158,7 +162,11 @@ export class SignatureV4
158162
request.query[AMZ_DATE_QUERY_PARAM] = longDate;
159163
request.query[EXPIRES_QUERY_PARAM] = ttl.toString(10);
160164

161-
const canonicalHeaders = getCanonicalHeaders(request, unsignableHeaders);
165+
const canonicalHeaders = getCanonicalHeaders(
166+
request,
167+
unsignableHeaders,
168+
signableHeaders
169+
);
162170
request.query[SIGNED_HEADERS_QUERY_PARAM] = getCanonicalHeaderList(
163171
canonicalHeaders
164172
);
@@ -205,14 +213,18 @@ export class SignatureV4
205213
credentials
206214
) as Promise<T>;
207215
} else {
208-
const { unsignableHeaders } = options as RequestSigningArguments;
216+
const {
217+
unsignableHeaders,
218+
signableHeaders
219+
} = options as RequestSigningArguments;
209220

210221
return this.signRequest(
211222
toSign as HttpRequest<any>,
212223
signingDate,
213224
region,
214225
credentials,
215-
unsignableHeaders
226+
unsignableHeaders,
227+
signableHeaders
216228
) as Promise<T>;
217229
}
218230
}
@@ -237,7 +249,8 @@ export class SignatureV4
237249
signingDate: DateInput,
238250
region: string,
239251
credentials: Credentials,
240-
unsignableHeaders?: Set<string>
252+
unsignableHeaders?: Set<string>,
253+
signableHeaders?: Set<string>
241254
): Promise<HttpRequest<any>> {
242255
const request = prepareRequest(originalRequest);
243256
const { longDate, shortDate } = formatDate(signingDate);
@@ -253,7 +266,11 @@ export class SignatureV4
253266
request.headers[SHA256_HEADER] = payloadHash;
254267
}
255268

256-
const canonicalHeaders = getCanonicalHeaders(request, unsignableHeaders);
269+
const canonicalHeaders = getCanonicalHeaders(
270+
request,
271+
unsignableHeaders,
272+
signableHeaders
273+
);
257274
const signature = await this.getSignature(
258275
longDate,
259276
scope,

Diff for: packages/signature-v4/src/getCanonicalHeaders.spec.ts

+26
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,30 @@ describe("getCanonicalHeaders", () => {
6161
host: "foo.us-east-1.amazonaws.com"
6262
});
6363
});
64+
65+
it("should allow specifying custom signable headers that override unsignable ones", () => {
66+
const request: HttpRequest<never> = {
67+
method: "POST",
68+
protocol: "https:",
69+
path: "/",
70+
headers: {
71+
host: "foo.us-east-1.amazonaws.com",
72+
foo: "bar",
73+
"user-agent": "foo-user"
74+
},
75+
hostname: "foo.us-east-1.amazonaws.com"
76+
};
77+
78+
expect(
79+
getCanonicalHeaders(
80+
request,
81+
new Set(["foo"]),
82+
new Set(["foo", "user-agent"])
83+
)
84+
).toEqual({
85+
host: "foo.us-east-1.amazonaws.com",
86+
foo: "bar",
87+
"user-agent": "foo-user"
88+
});
89+
});
6490
});

Diff for: packages/signature-v4/src/getCanonicalHeaders.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
*/
1111
export function getCanonicalHeaders(
1212
{ headers }: HttpRequest<any>,
13-
unsignableHeaders?: Set<string>
13+
unsignableHeaders?: Set<string>,
14+
signableHeaders?: Set<string>
1415
): HeaderBag {
1516
const canonical: HeaderBag = {};
1617
for (let headerName of Object.keys(headers).sort()) {
@@ -21,7 +22,12 @@ export function getCanonicalHeaders(
2122
PROXY_HEADER_PATTERN.test(canonicalHeaderName) ||
2223
SEC_HEADER_PATTERN.test(canonicalHeaderName)
2324
) {
24-
continue;
25+
if (
26+
!signableHeaders ||
27+
(signableHeaders && !signableHeaders.has(canonicalHeaderName))
28+
) {
29+
continue;
30+
}
2531
}
2632

2733
canonical[canonicalHeaderName] = headers[headerName]

Diff for: packages/types/src/signature.ts

+10
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ export interface RequestSigningArguments extends SigningArguments {
2323
* lower case and then checked for existence in the unsignableHeaders set.
2424
*/
2525
unsignableHeaders?: Set<string>;
26+
27+
/**
28+
* A set of strings whose members represents headers that should be signed.
29+
* Any values passed here will override those provided via unsignableHeaders,
30+
* allowing them to be signed.
31+
*
32+
* All headers in the provided request will have their names converted to
33+
* lower case before signing.
34+
*/
35+
signableHeaders?: Set<string>;
2636
}
2737

2838
export interface RequestPresigner {

0 commit comments

Comments
 (0)