Skip to content

Instantly share code, notes, and snippets.

@farrellit
Created February 10, 2025 16:45
Show Gist options
  • Save farrellit/6ba95abe577afa04bc79519af5e6cdc5 to your computer and use it in GitHub Desktop.
Save farrellit/6ba95abe577afa04bc79519af5e6cdc5 to your computer and use it in GitHub Desktop.
Javascript AWS Signature V4 with Web Crypto based Hashing
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