in src/queue/authentication/QueueSharedKeyAuthenticator.ts [17:169]
public async validate(
req: IRequest,
context: Context
): Promise<boolean | undefined> {
const queueContext = new QueueStorageContext(context);
const account = queueContext.account!;
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Start validation against account shared key authentication.`,
queueContext.contextID
);
const authHeaderValue = req.getHeader(HeaderConstants.AUTHORIZATION);
if (authHeaderValue === undefined) {
this.logger.info(
// tslint:disable-next-line:max-line-length
`QueueSharedKeyAuthenticator:validate() Request doesn't include valid authentication header. Skip shared key authentication.`,
queueContext.contextID
);
return undefined;
}
// TODO: Make following async
const accountProperties = this.dataStore.getAccount(account);
if (accountProperties === undefined) {
this.logger.error(
`QueueSharedKeyAuthenticator:validate() Invalid storage account ${account}.`,
queueContext.contextID
);
throw StorageErrorFactory.ResourceNotFound(
context.contextID!
);
}
const authType = authHeaderValue.split(" ")[0] as
| "SharedKey"
| "SharedKeyLite";
const authValue = authHeaderValue.split(" ")[1];
const headersToSign = this.getHeadersToSign(authType, req);
const stringToSign: string =
headersToSign +
this.getCanonicalizedResourceString(
authType,
req,
account,
queueContext.authenticationPath
);
this.logger.info(
`QueueSharedKeyAuthenticator:validate() [STRING TO SIGN]:${JSON.stringify(
stringToSign
)}`,
queueContext.contextID
);
const signature1 = computeHMACSHA256(stringToSign, accountProperties.key1);
const authValue1 = `${account}:${signature1}`;
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Calculated authentication header based on key1: ${authValue1}`,
queueContext.contextID
);
if (authValue === authValue1) {
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Signature 1 matched.`,
queueContext.contextID
);
return true;
}
if (accountProperties.key2) {
const signature2 = computeHMACSHA256(
stringToSign,
accountProperties.key2
);
const authValue2 = `${account}:${signature2}`;
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Calculated authentication header based on key2: ${authValue2}`,
queueContext.contextID
);
if (authValue === authValue2) {
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Signature 2 matched.`,
queueContext.contextID
);
return true;
}
}
if (context.context.isSecondary && queueContext.authenticationPath?.indexOf(account) === 1)
{
// JS/.net Track2 SDK will generate stringToSign from IP style URI with "-secondary" in authenticationPath, so will also compare signature with this kind stringToSign
const stringToSign_secondary: string =
headersToSign +
this.getCanonicalizedResourceString(
authType,
req,
account,
// The authenticationPath looks like "/devstoreaccount1/queue", add "-secondary" after account name to "/devstoreaccount1-secondary/queue"
queueContext.authenticationPath?.replace(account, account + "-secondary")
);
this.logger.info(
`QueueSharedKeyAuthenticator:validate() [STRING TO SIGN_secondary]:${JSON.stringify(
stringToSign_secondary
)}`,
queueContext.contextID
);
const signature1_secondary = computeHMACSHA256(stringToSign_secondary, accountProperties.key1);
const authValue1_secondary = `${account}:${signature1_secondary}`;
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Calculated authentication header based on key1: ${authValue1_secondary}`,
queueContext.contextID
);
if (authValue === authValue1_secondary) {
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Signature 1_secondary matched.`,
queueContext.contextID
);
return true;
}
if (accountProperties.key2) {
const signature2_secondary = computeHMACSHA256(
stringToSign_secondary,
accountProperties.key2
);
const authValue2_secondary = `${account}:${signature2_secondary}`;
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Calculated authentication header based on key2: ${authValue2_secondary}`,
queueContext.contextID
);
if (authValue === authValue2_secondary) {
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Signature 2_secondary matched.`,
queueContext.contextID
);
return true;
}
}
}
// this.logger.info(`[URL]:${req.getUrl()}`);
// this.logger.info(`[HEADERS]:${req.getHeaders().toString()}`);
// this.logger.info(`[KEY]: ${request.headers.get(HeaderConstants.AUTHORIZATION)}`);
this.logger.info(
`QueueSharedKeyAuthenticator:validate() Validation failed.`,
queueContext.contextID
);
return false;
}