diff --git a/src/auth/SignHelper.js b/src/auth/SignHelper.js deleted file mode 100644 index 9a69c33..0000000 --- a/src/auth/SignHelper.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -/** - * ProductAdvertisingAPI - * https://webservices.amazon.com/paapi5/documentation/index.html - * - * This file is for signing PAAPI request with AWS V4 Signing. For more details, see - * https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html - * - * Do not edit the class manually. - * - */ - -'use strict'; - -// sources of inspiration: -// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html - -var crypto = require('crypto-js'); - -exports.createAuthorizationHeader = function( - accessKey, - secretKey, - requestHeaders, - httpMethod, - path, - payload, - region, - service, - timestamp -) { - /* Step 1: Create Signed Headers */ - var signedHeaders = exports.createSignedHeaders(requestHeaders); - - /* Step 2: Create Canonical Request */ - var canonicalRequest = exports.createCanonicalRequest(httpMethod, path, {}, requestHeaders, payload); - - /* Step 3: Create String To Sign */ - var stringToSign = exports.createStringToSign(timestamp, region, service, canonicalRequest); - - /* Step 4: Create Signature Headers */ - var signature = exports.createSignature(secretKey, timestamp, region, service, stringToSign); - - /* Step 5: Create Authorization Header */ - var authorizationHeader = exports.createAuthorizationHeaders( - timestamp, - accessKey, - region, - service, - signedHeaders, - signature - ); - - return authorizationHeader; -}; - -exports.createAuthorizationHeaders = function(timestamp, accessKey, region, service, signedHeaders, signature) { - return ( - 'AWS4-HMAC-SHA256' + - ' ' + - 'Credential=' + - accessKey + - '/' + - exports.createCredentialScope(timestamp, region, service) + - ', ' + - 'SignedHeaders=' + - signedHeaders + - ', ' + - 'Signature=' + - signature - ); -}; - -exports.createCanonicalRequest = function(method, pathname, query, headers, payload) { - var payloadJson = JSON.stringify(payload); - return [ - method.toUpperCase(), - pathname, - exports.createCanonicalQueryString(query), - exports.createCanonicalHeaders(headers), - exports.createSignedHeaders(headers), - hexEncodedHash(String(payloadJson)) - ].join('\n'); -}; - -exports.createCanonicalQueryString = function(params) { - return Object.keys(params) - .sort() - .map(function(key) { - return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); - }) - .join('&'); -}; - -exports.createCanonicalHeaders = function(headers) { - return Object.keys(headers) - .sort() - .map(function(name) { - return name.toLowerCase().trim() + ':' + headers[name].toString().trim() + '\n'; - }) - .join(''); -}; - -exports.createSignedHeaders = function(headers) { - return Object.keys(headers) - .sort() - .map(function(name) { - return name.toLowerCase().trim(); - }) - .join(';'); -}; - -exports.createCredentialScope = function(time, region, service) { - return [toDate(time), region, service, 'aws4_request'].join('/'); -}; - -exports.createStringToSign = function(time, region, service, request) { - return [ - 'AWS4-HMAC-SHA256', - toTime(time), - exports.createCredentialScope(time, region, service), - hexEncodedHash(request) - ].join('\n'); -}; - -exports.createSignature = function(secret, time, region, service, stringToSign) { - var h1 = hmac('AWS4' + secret, toDate(time)); // date-key - var h2 = hmac(h1, region); // region-key - var h3 = hmac(h2, service); // service-key - var h4 = hmac(h3, 'aws4_request'); // signing-key - return hmac(h4, stringToSign).toString(crypto.enc.Hex); -}; - -exports.toAmzDate = function(time) { - return new Date(time).toISOString().replace(/[:\-]|\.\d{3}/g, ''); -}; - -function toTime(time) { - return new Date(time).toISOString().replace(/[:\-]|\.\d{3}/g, ''); -} - -function toDate(time) { - return toTime(time).substring(0, 8); -} - -function hmac(key, data) { - return crypto.HmacSHA256(data, key); -} - -function hexEncodedHash(data) { - return crypto.SHA256(data).toString(crypto.enc.Hex); -} diff --git a/src/auth/SignHelper.ts b/src/auth/SignHelper.ts new file mode 100644 index 0000000..99b2a20 --- /dev/null +++ b/src/auth/SignHelper.ts @@ -0,0 +1,123 @@ +/** + * Adapted to TypeScript by David A. Ball. (c) 2024. + * + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * ProductAdvertisingAPI + * https://webservices.amazon.com/paapi5/documentation/index.html + * + * This file is for signing PAAPI request with AWS V4 Signing. For more details, see + * https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html + * + * Do not edit the class manually. + * + */ + +// sources of inspiration: +// http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html + +import crypto from 'crypto-js'; + +const AWS4_HMAC_SHA256 = 'AWS4-HMAC-SHA256'; + +const toTime = (time: number | string | Date) => new Date(time).toISOString().replace(/[:\-]|\.\d{3}/g, ''); +const toDate = (time: number | string | Date) => toTime(time).substring(0, 8); +const hmac = (key: crypto.lib.WordArray | string, data: crypto.lib.WordArray | string) => crypto.HmacSHA256(data, key); +const hexEncodedHash = (data: crypto.lib.WordArray | string) => crypto.SHA256(data).toString(crypto.enc.Hex); + +export const createAuthorizationHeaders = (timestamp: number | string | Date, accessKey: string, region: string, service: string, signedHeaders: string, signature: string) => + `${AWS4_HMAC_SHA256} Credential=${accessKey}/${exports.createCredentialScope(timestamp, region, service)}, SignedHeaders=${signedHeaders}, Signature=${signature}`; + +export const createCanonicalRequest = (method: string, pathname: string, query: Record, headers: Record, payload: any) => { + var payloadJson = JSON.stringify(payload); + return [ + method.toUpperCase(), + pathname, + exports.createCanonicalQueryString(query), + exports.createCanonicalHeaders(headers), + exports.createSignedHeaders(headers), + hexEncodedHash(String(payloadJson)) + ].join('\n'); +}; + +export const createCanonicalQueryString = (params: Record) => { + return Object.keys(params) + .sort() + .map(function(key) { + return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`; + }) + .join('&'); +}; + +export const createCanonicalHeaders = (headers: Record) => + Object.keys(headers) + .sort() + .map(function(name: string) { + return `${name.toLowerCase().trim()}:${headers[name].toString().trim()}\n`; + }) + .join(''); + +export const createSignedHeaders = (headers: Record) => + Object.keys(headers) + .sort() + .map(function(name: string) { + return name.toLowerCase().trim(); + }) + .join(';'); + +export const createCredentialScope = (time: number | string | Date, region: string, service: string) => + [toDate(time), region, service, 'aws4_request'].join('/'); + +export const createStringToSign = (time: number | string | Date, region: string, service: string, request: string) => + [ + AWS4_HMAC_SHA256, + toTime(time), + exports.createCredentialScope(time, region, service), + hexEncodedHash(request) + ].join('\n'); + +export const createSignature = (secret: string, time: number | string | Date, region: string, service: string, stringToSign: string) => { + const AWS4 = 'AWS4'; + const AWS4_REQUEST = 'aws4_request'; + var h1 = hmac(`${AWS4}${secret}`, toDate(time)); // date-key + var h2 = hmac(h1, region); // region-key + var h3 = hmac(h2, service); // service-key + var h4 = hmac(h3, AWS4_REQUEST); // signing-key + return hmac(h4, stringToSign).toString(crypto.enc.Hex); +}; + +export const toAmzDate = (time: number | string | Date) => new Date(time).toISOString().replace(/[:\-]|\.\d{3}/g, ''); + +export const createAuthorizationHeader = (accessKey: string, secretKey: string, requestHeaders: Record, httpMethod: string, path: string, payload: Record, region: string, service: string, timestamp: number | string | Date) => { + /* Step 1: Create Signed Headers */ + const signedHeaders = createSignedHeaders(requestHeaders); + /* Step 2: Create Canonical Request */ + const canonicalRequest = createCanonicalRequest(httpMethod, path, {}, requestHeaders, payload); + /* Step 3: Create String To Sign */ + const stringToSign = createStringToSign(timestamp, region, service, canonicalRequest); + /* Step 4: Create Signature Headers */ + const signature = createSignature(secretKey, timestamp, region, service, stringToSign); + /* Step 5: Create Authorization Header */ + const authorizationHeader = createAuthorizationHeaders( + timestamp, + accessKey, + region, + service, + signedHeaders, + signature + ); + return authorizationHeader; +} \ No newline at end of file