@aws-sdk/core
Version:
Core functions & classes shared by multiple AWS SDK clients.
101 lines (100 loc) • 5.25 kB
JavaScript
import { HttpRequest } from "@smithy/core/protocols";
import { SMITHY_CONTEXT_KEY } from "@smithy/types";
import { getAgeHeader, getDateHeader, getSkewCorrectedDate, getUpdatedSystemClockOffset } from "../utils";
const throwSigningPropertyError = (name, property) => {
if (!property) {
throw new Error(`Property \`${name}\` is not resolved for AWS SDK SigV4Auth`);
}
return property;
};
const hasEventStreamOutput = (signingProperties) => {
const context = signingProperties.context;
const eventStream = context?.[SMITHY_CONTEXT_KEY]
?.eventStream;
return eventStream?.output === true;
};
export const validateSigningProperties = async (signingProperties) => {
const context = throwSigningPropertyError("context", signingProperties.context);
const config = throwSigningPropertyError("config", signingProperties.config);
const authScheme = context.endpointV2?.properties?.authSchemes?.[0];
const signerFunction = throwSigningPropertyError("signer", config.signer);
const signer = await signerFunction(authScheme);
const signingRegion = signingProperties?.signingRegion;
const signingRegionSet = signingProperties?.signingRegionSet;
const signingName = signingProperties?.signingName;
return {
config,
signer,
signingRegion,
signingRegionSet,
signingName,
};
};
export class AwsSdkSigV4Signer {
async sign(httpRequest, identity, signingProperties) {
if (!HttpRequest.isInstance(httpRequest)) {
throw new Error("The request is not an instance of `HttpRequest` and cannot be signed");
}
const validatedProps = await validateSigningProperties(signingProperties);
const { config, signer } = validatedProps;
let { signingRegion, signingName } = validatedProps;
const handlerExecutionContext = signingProperties.context;
if (handlerExecutionContext?.authSchemes?.length ?? 0 > 1) {
const [first, second] = handlerExecutionContext.authSchemes;
if (first?.name === "sigv4a" && second?.name === "sigv4") {
signingRegion = second?.signingRegion ?? signingRegion;
signingName = second?.signingName ?? signingName;
}
}
const noSkewCorrection = (await config.disableClockSkewCorrection?.()) === true;
signingProperties._disableClockSkewCorrection = noSkewCorrection;
const skipSkewSampling = noSkewCorrection || hasEventStreamOutput(signingProperties);
signingProperties._skipClockSkewSampling = skipSkewSampling;
if (!skipSkewSampling) {
signingProperties._preRequestSystemClockOffset = config.systemClockOffset;
signingProperties._requestSentAt = Date.now();
}
const signedRequest = await signer.sign(httpRequest, {
signingDate: noSkewCorrection ? new Date() : getSkewCorrectedDate(config.systemClockOffset),
signingRegion: signingRegion,
signingService: signingName,
});
return signedRequest;
}
errorHandler(signingProperties) {
return (error) => {
const errorException = error;
if (!signingProperties._skipClockSkewSampling && !signingProperties._disableClockSkewCorrection) {
const serverTime = errorException.ServerTime ?? getDateHeader(errorException.$response);
if (serverTime) {
const config = throwSigningPropertyError("config", signingProperties.config);
const preRequestOffset = signingProperties._preRequestSystemClockOffset;
const timeRequestSent = signingProperties._requestSentAt;
const ageHeader = getAgeHeader(errorException.$response);
const newOffset = getUpdatedSystemClockOffset(serverTime, config.systemClockOffset, timeRequestSent, ageHeader);
config.systemClockOffset = newOffset;
const skewExceedsThreshold = Math.abs(newOffset) >= 240_000;
const isLocalCorrection = newOffset !== preRequestOffset;
const isConcurrentCorrection = preRequestOffset !== undefined && preRequestOffset !== newOffset;
if (skewExceedsThreshold && (isLocalCorrection || isConcurrentCorrection) && errorException.$metadata) {
errorException.$metadata.clockSkewCorrected = true;
}
}
}
throw error;
};
}
successHandler(httpResponse, signingProperties) {
if (signingProperties._skipClockSkewSampling || signingProperties._disableClockSkewCorrection) {
return;
}
const dateHeader = getDateHeader(httpResponse);
if (dateHeader) {
const config = throwSigningPropertyError("config", signingProperties.config);
const timeRequestSent = signingProperties._requestSentAt;
const ageHeader = getAgeHeader(httpResponse);
config.systemClockOffset = getUpdatedSystemClockOffset(dateHeader, config.systemClockOffset, timeRequestSent, ageHeader);
}
}
}
export const AWSSDKSigV4Signer = AwsSdkSigV4Signer;