const crypto = require('crypto');
class AwsV4 {
constructor(accessKeyID, secretAccessKey) {
this.accessKeyID = accessKeyID;
this.secretAccessKey = secretAccessKey;
this.currentDateObject = new Date();
this.xAmzDate = this.getTimeStamp(this.currentDateObject);
this.currentDate = this.getDate(this.currentDateObject);
}
setPath(path) {
this.path = path;
}
setServiceName(serviceName) {
this.serviceName = serviceName;
}
setRegionName(regionName) {
this.regionName = regionName;
}
setPayload(payload) {
this.payload = payload;
}
setRequestMethod(method) {
this.httpMethodName = method;
}
addHeader(headerName, headerValue) {
this.awsHeaders = this.awsHeaders || {};
this.awsHeaders[headerName] = headerValue;
}
prepareCanonicalRequest() {
let canonicalURL = '';
canonicalURL += this.httpMethodName + '\n';
canonicalURL += this.path + '\n';
// Add the missing line to include the CanonicalQueryString
canonicalURL += '' + '\n'; // Use an empty string as there are no query string parameters in this case
let signedHeaders = '';
// Add x-amz-date header
this.addHeader('x-amz-date', this.xAmzDate);
// Sort headers lexicographically by header name (lowercase)
const sortedHeaderKeys = Object.keys(this.awsHeaders).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
for (const key of sortedHeaderKeys) {
if (key !== 'Accept' && key !== 'Accept-Language' && key !== 'Content-Type') {
signedHeaders += key.toLowerCase() + ';';
canonicalURL += key.toLowerCase() + ':' + this.awsHeaders[key] + '\n';
}
}
canonicalURL += '\n';
this.strSignedHeader = signedHeaders.slice(0, -1);
canonicalURL += this.strSignedHeader + '\n';
canonicalURL += this.generateHex(this.payload);
return canonicalURL;
}
prepareStringToSign(canonicalURL) {
let stringToSign = '';
stringToSign += 'AWS4-HMAC-SHA256' + '\n';
stringToSign += this.xAmzDate + '\n';
stringToSign += this.currentDate + '/' + this.regionName + '/' + this.serviceName + '/' + 'aws4_request' + '\n';
stringToSign += this.generateHex(canonicalURL);
return stringToSign;
}
calculateSignature(stringToSign) {
const signatureKey = this.getSignatureKey(this.secretAccessKey, this.currentDate, this.regionName, this.serviceName);
const signature = crypto.createHmac('sha256', signatureKey).update(stringToSign).digest('hex');
return signature;
}
getHeaders() {
const canonicalURL = this.prepareCanonicalRequest();
const stringToSign = this.prepareStringToSign(canonicalURL);
const signature = this.calculateSignature(stringToSign);
const authorizationHeader = this.buildAuthorizationString(signature);
this.awsHeaders['Authorization'] = authorizationHeader;
this.awsHeaders['x-amz-date'] = this.xAmzDate;
return this.awsHeaders;
}
getUpdatedHeaders() {
this.setPath('/paapi5/getitems');
this.setServiceName('ProductAdvertisingAPI');
this.setRegionName('us-east-1');
this.setRequestMethod('POST');
this.setPayload(payloadJsonString); // Use the actual payload JSON string
this.addHeader('Host', 'webservices.amazon.com');
this.addHeader('Content-Encoding', 'amz-1.0');
this.addHeader('Content-Type', 'application/json; charset=UTF-8');
this.addHeader('x-amz-date', this.xAmzDate); // Move this line up
this.addHeader('X-Amz-Target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.GetItems');
const headers = this.getHeaders();
return {
'Authorization': headers['Authorization'],
'X-Amz-Date': headers['x-amz-date']
};
}
buildAuthorizationString(signature) {
return 'AWS4-HMAC-SHA256' + ' ' + 'Credential=' + this.accessKeyID + '/' + this.getDate(this.currentDateObject) + '/' + this.regionName + '/' + this.serviceName + '/' + 'aws4_request' + ' ' + 'SignedHeaders=' + this.strSignedHeader + ' ' + 'Signature=' + signature;
}
generateHex(data) {
return crypto.createHash('sha256').update(data).digest('hex');
}
getSignatureKey(key, date, regionName, serviceName) {
const kSecret = 'AWS4' + key;
const kDate = crypto.createHmac('sha256', kSecret).update(date).digest();
const kRegion = crypto.createHmac('sha256', kDate).update(regionName).digest();
const kService = crypto.createHmac('sha256', kRegion).update(serviceName).digest();
const kSigning = crypto.createHmac('sha256', kService).update('aws4_request').digest();
return kSigning;
}
getTimeStamp(date) {
return date.toISOString().replace(/[:-]|\.\d{3}/g, '');
}
getDate(date) {
const year = date.getUTCFullYear();
const month = ('0' + (date.getUTCMonth() + 1)).slice(-2);
const day = ('0' + date.getUTCDate()).slice(-2);
return `${year}${month}${day}`;
}
}
const awsV4 = new AwsV4('AKIAI6QL7ST37VECNI7A', 'ZnZS++sxYuDGxP8VOSEG2uZd8Qmtup9F51wHgOkw');
const payload = {
"ItemIds": [
"B01M6V8CP4"
],
"Resources": [
"CustomerReviews.Count",
"CustomerReviews.StarRating",
"Images.Variants.Large",
"ItemInfo.Features",
"Offers.Listings.Promotions",
"Offers.Summaries.LowestPrice"
],
"PartnerTag": "timstools03-20",
"PartnerType": "Associates",
"Marketplace": "www.amazon.com"
};
const payloadJsonString = JSON.stringify(payload);
// Pass the JSON string to setPayload()
awsV4.setPayload(payloadJsonString);
const updatedHeaders = awsV4.getUpdatedHeaders();
console.log(updatedHeaders);
I did notice that after posting. But I deleted them immediately after I sent it. I am looking for a solution to use the PA API on my Bubble site but the SDK is just not a good option. Only need a script to build the authorization and x-amz-date headers. It seems I am making an error somewhere but I don't get much feedback on what the actual error is.