Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import axios from 'axios';
- import utils from './utils';
- const AWS_SHA_256 = 'AWS4-HMAC-SHA256';
- const AWS4_REQUEST = 'aws4_request';
- const X_AMZ_DATE = 'x-amz-date';
- const X_AMZ_SECURITY_TOKEN = 'x-amz-security-token';
- const HOST = 'host';
- const AUTHORIZATION = 'Authorization';
- const textEncoder = new TextEncoder();
- /**
- * Converts Uint8Array to a hex string.
- */
- const toHex = (uint8Array) =>
- [...uint8Array].map((b) => b.toString(16).padStart(2, '0')).join('');
- /**
- * Performs SHA-256 hashing.
- */
- const sha256 = async (str) => {
- const hashBuffer = await crypto.subtle.digest('SHA-256', textEncoder.encode(str));
- return new Uint8Array(hashBuffer);
- };
- /**
- * Generates HMAC-SHA256 signature.
- */
- const hmacSha256 = async (key, data) => {
- const keyBytes = typeof key === 'string' ? textEncoder.encode(key) : key;
- const cryptoKey = await crypto.subtle.importKey(
- 'raw',
- keyBytes,
- { name: 'HMAC', hash: 'SHA-256' },
- false,
- ['sign']
- );
- return new Uint8Array(await crypto.subtle.sign('HMAC', cryptoKey, textEncoder.encode(data)));
- };
- /**
- * Derives AWS SigV4 signing key.
- */
- const calculateSigningKey = async (secretKey, date, region, service) => {
- let kDate = await hmacSha256(`AWS4${secretKey}`, date);
- let kRegion = await hmacSha256(kDate, region);
- let kService = await hmacSha256(kRegion, service);
- return hmacSha256(kService, AWS4_REQUEST);
- };
- /**
- * Builds the canonical request for AWS Signature v4.
- */
- const buildCanonicalRequest = async (method, path, queryParams, headers, payload) => {
- const canonicalUri = encodeURI(path);
- const canonicalQueryString = new URLSearchParams(queryParams).toString();
- const canonicalHeaders = Object.keys(headers)
- .sort()
- .map((k) => `${k.toLowerCase()}:${headers[k]}`)
- .join('\n') + '\n';
- const signedHeaders = Object.keys(headers)
- .map((k) => k.toLowerCase())
- .sort()
- .join(';');
- const payloadHashHex = toHex(await sha256(payload || ''));
- return `${method}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHashHex}`;
- };
- /**
- * Builds the AWS String to Sign.
- */
- const buildStringToSign = (datetime, credentialScope, hashedCanonicalRequest) =>
- `${AWS_SHA_256}\n${datetime}\n${credentialScope}\n${hashedCanonicalRequest}`;
- /**
- * Generates the AWS credential scope.
- */
- const buildCredentialScope = (datetime, region, service) =>
- `${datetime.slice(0, 8)}/${region}/${service}/${AWS4_REQUEST}`;
- /**
- * Constructs the AWS authorization header.
- */
- const buildAuthorizationHeader = (accessKey, credentialScope, headers, signature) =>
- `${AWS_SHA_256} Credential=${accessKey}/${credentialScope}, SignedHeaders=${Object.keys(headers)
- .map((k) => k.toLowerCase())
- .sort()
- .join(';')}, Signature=${signature}`;
- const sigV4ClientFactory = {
- newClient: (config) => {
- utils.assertDefined(config.accessKey, 'accessKey');
- utils.assertDefined(config.secretKey, 'secretKey');
- utils.assertDefined(config.serviceName, 'serviceName');
- utils.assertDefined(config.region, 'region');
- utils.assertDefined(config.endpoint, 'endpoint');
- return {
- makeRequest: async ({ verb, path, queryParams = {}, headers = {}, body }) => {
- headers = utils.copy(headers) || {}; // Ensure headers object exists
- // Set default headers if missing
- headers['Content-Type'] = headers['Content-Type'] || config.defaultContentType || 'application/json';
- headers.Accept = headers.Accept || config.defaultAcceptType || 'application/json';
- if (verb === 'GET') body = '';
- else body = body ? JSON.stringify(body) : '';
- const datetime = new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
- headers[X_AMZ_DATE] = datetime;
- headers[HOST] = new URL(config.endpoint).hostname;
- // Build AWS Signature
- const canonicalRequest = await buildCanonicalRequest(verb, path, queryParams, headers, body);
- const hashedCanonicalRequest = toHex(await sha256(canonicalRequest));
- const credentialScope = buildCredentialScope(datetime, config.region, config.serviceName);
- const stringToSign = buildStringToSign(datetime, credentialScope, hashedCanonicalRequest);
- const signingKey = await calculateSigningKey(config.secretKey, datetime.slice(0, 8), config.region, config.serviceName);
- const signature = toHex(await hmacSha256(signingKey, stringToSign));
- // Attach headers
- headers[AUTHORIZATION] = buildAuthorizationHeader(config.accessKey, credentialScope, headers, signature);
- if (config.sessionToken) headers[X_AMZ_SECURITY_TOKEN] = config.sessionToken;
- // Construct request URL
- let url = `${config.endpoint}${path}`;
- const queryString = utils.parseParametersToObject(queryParams, Object.keys(queryParams));
- if (queryString) url += `?${new URLSearchParams(queryString).toString()}`;
- return axios({ method: verb, url, headers, data: body });
- },
- };
- },
- };
- export default sigV4ClientFactory;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement