Created
February 10, 2025 16:45
-
-
Save farrellit/6ba95abe577afa04bc79519af5e6cdc5 to your computer and use it in GitHub Desktop.
Javascript AWS Signature V4 with Web Crypto based Hashing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async awsFetch(creds, url, method , data, region, operation, content_type, accept) { | |
/* | |
creds: a Credentials contains SessionToken SecretAccessKey and AcccessKeyId | |
url: a URL | |
method: request method | |
data: request data | |
region: aws region | |
operation: aws operation (maybe execute-api?) | |
content_type: header | |
accept: header | |
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html | |
*/ | |
// 1. prepare and hash request | |
if(! [ "POST", "PATCH", "PUT" ].includes(method) ){ | |
data = "" | |
} | |
const request_date = new Date() | |
const request_datestr = request_date.toISOString().replaceAll(/([-:]|\.\d+)/g, '') | |
const request_dayonly = request_date.toISOString().replace(/T.*/, '').replaceAll("-","") | |
const hashedPayload=await sha256HexDigestText(data) | |
const signed_headers = ["host","x-amz-date","x-amz-security-token"] | |
const canonicalRequest=[ | |
method, | |
url.pathname, | |
url.searchParams.toString(), | |
`host:${url.host}`, | |
`x-amz-date:${request_datestr}`, | |
`x-amz-security-token:${creds.SessionToken}`, | |
"", | |
signed_headers.join(";"), | |
hashedPayload, | |
].join("\n") | |
console.log("canonicalRequest", canonicalRequest) | |
const hashedCanonicalRequest=await sha256HexDigestText(canonicalRequest) | |
const stringToSign = [ | |
"AWS4-HMAC-SHA256", | |
request_datestr, | |
`${request_dayonly}/${region}/${operation}/aws4_request`, | |
hashedCanonicalRequest, | |
].join("\n") | |
console.log("string to sign", stringToSign) | |
// hmac signing | |
const kDate = await hash(textToArrayBuffer('AWS4' + creds.SecretAccessKey), request_dayonly) | |
const kDateRegionKey = await hash(kDate, region) | |
const kDateRegionServiceKey = await hash(kDateRegionKey, operation) | |
const kSigningKey = await hash(kDateRegionServiceKey, "aws4_request") | |
// final hash. TODO why is this not using hash() ? | |
const key = await crypto.subtle.importKey("raw", kSigningKey, { name: "HMAC", hash: "SHA-256" }, false, [ "sign" ]) | |
const signature = await arrayBufferToHex(await crypto.subtle.sign("HMAC", key, textToArrayBuffer(stringToSign))) | |
console.log("signed result", signature) | |
// now generate the auth header | |
const authHeader = `AWS4-HMAC-SHA256 Credential=${creds.AccessKeyId}/${request_dayonly}/${region}/${operation}/aws4_request, SignedHeaders=${signed_headers.join(";")}, Signature=${signature}` | |
// now we can make the request | |
const params = {method: method, headers:{ | |
Authorization: authHeader, | |
Accept: accept, | |
"Content-Type": content_type, | |
host: url.host, | |
"x-amz-security-token": creds.SessionToken, | |
"x-amz-date": request_datestr, | |
} } | |
if( data ) { | |
console.log("includes method") | |
params.body = data | |
} | |
return fetch(url, params ) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment