I’m working on creating a lambda to query and dynamically stand up AWS managed Prometheus alarms. To do so, I need to make a signed http request and I’m getting this error:
“”statusCode”: 403, “headers”: “{n “date”: “Mon, 24 Jun 2024 12:00:23 GMT”,n “content-type”: “application/json”,n “content-length”: “1867”,n “connection”: “keep-alive”,n “x-amzn-requestid”: “7a6bc218-9f69-42ac-b2f9-cfb0b0832acf, 7a6bc218-9f69-42ac-b2f9-cfb0b0832acf”,n “server”: “amazon”,n “x-amzn-errortype”: “InvalidSignatureException”n}”, “body”: “{“message”:”The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.nnThe Canonical String for this request should have been”
The logging is very verbose and I’ll remove the sensitive logging once I move outside my test env.I’m also using a custom logging lib so you can ignore that part. I thought I had this configured correctly but now I’m getting stuck.
That being said, am I formatting this request wrong? Any ideas?
Here my code:
import {SignatureV4} from '@smithy/signature-v4';
import {defaultProvider} from '@aws-sdk/credential-provider-node';
import {HttpRequest} from '@smithy/protocol-http';
import {Sha256} from '@aws-crypto/sha256-browser';
const makeSignedRequest = async (
path: string,
region: string
): Promise<any> => {
const hostname = `aps-workspaces.${region}.amazonaws.com`;
const request = new HttpRequest({
method: 'GET',
protocol: 'https:',
hostname,
path,
headers: {
'Content-Type': 'application/json',
Host: hostname, // Explicitly adding the Host header
},
});
request.headers.Host = hostname; // Explicitly adding the Host header
log
.info()
.str('initialRequest', JSON.stringify(request, null, 2))
.msg('Initial request object');
const signer = new SignatureV4({
credentials: defaultProvider(),
region,
service: 'aps',
sha256: Sha256,
});
const signedRequest = await signer.sign(request);
log
.info()
.str(
'signedRequest',
JSON.stringify(
{
method: signedRequest.method,
protocol: signedRequest.protocol,
hostname: signedRequest.hostname,
path: signedRequest.path,
headers: signedRequest.headers,
},
null,
2
)
)
.msg('Signed request object');
return new Promise((resolve, reject) => {
const req = https.request(
{
hostname: signedRequest.hostname,
path: signedRequest.path,
method: signedRequest.method,
headers: signedRequest.headers,
},
res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
log
.info()
.num('statusCode', res.statusCode || 0)
.str('headers', JSON.stringify(res.headers, null, 2))
.str('body', data)
.msg('Response received');
try {
const parsedData = JSON.parse(data);
resolve(parsedData);
} catch (error) {
log
.error()
.err(error)
.str('rawData', data)
.msg('Failed to parse response data');
reject(error);
}
});
}
);
req.on('error', error => {
log.error().err(error).msg('Request error');
reject(error);
});
req.end();
});
};const makeSignedRequest = async (
path: string,
region: string
): Promise<any> => {
const hostname = `aps-workspaces.${region}.amazonaws.com`;
const request = new HttpRequest({
method: 'GET',
protocol: 'https:',
hostname,
path,
headers: {
'Content-Type': 'application/json',
Host: hostname,
},
});
request.headers.Host = hostname;
log
.info()
.str('initialRequest', JSON.stringify(request, null, 2))
.msg('Initial request object');
const signer = new SignatureV4({
credentials: defaultProvider(),
region,
service: 'aps',
sha256: Sha256,
});
const signedRequest = await signer.sign(request);
log
.info()
.str(
'signedRequest',
JSON.stringify(
{
method: signedRequest.method,
protocol: signedRequest.protocol,
hostname: signedRequest.hostname,
path: signedRequest.path,
headers: signedRequest.headers,
},
null,
2
)
)
.msg('Signed request object');
return new Promise((resolve, reject) => {
const req = https.request(
{
hostname: signedRequest.hostname,
path: signedRequest.path,
method: signedRequest.method,
headers: signedRequest.headers,
},
res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
log
.info()
.num('statusCode', res.statusCode || 0)
.str('headers', JSON.stringify(res.headers, null, 2))
.str('body', data)
.msg('Response received');
try {
const parsedData = JSON.parse(data);
resolve(parsedData);
} catch (error) {
log
.error()
.err(error)
.str('rawData', data)
.msg('Failed to parse response data');
reject(error);
}
});
}
);
req.on('error', error => {
log.error().err(error).msg('Request error');
reject(error);
});
req.end();
});
};
The signed request should be pulling the correct creds and signing the req but for the life of me I can’t see where I’m going wrong.
William Rose is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.