Closed ramsenconstantine closed 3 years ago
No idea what supertest
is I'm afraid – I suspect the problem is with how you're using that.
Try using plain Node.js requests like in the README and see if you can track down the issue.
On initial glance it looks like you're using a session token, but not signing the request with it. See the README for details on how to include it: https://github.com/mhart/aws4#aws4signrequestoptions-credentials
I have tried with the session token in aws4.sign as well, but it doesn't make a difference. I can try another method of hitting the endpoint.
This looks suspicious: host: url
I don't know the value of url
– but a URL is not a host
One thing I should mention is the code worked in July and I just revisited it. In my case what is passed as url is something like n1sd4n2bl9.execute-api.us-west-1.amazonaws.com.
The code you posted shouldn't have been working, even in July – not without the session token in the signature
It is odd, but I'm looking at my old jenkins logs and comparing them to my github check ins at the time. That code was working for whatever reason.
I guess if the session token was undefined, then it might've worked.
In any case, this appears to be a problem with a third-party library (supertest).
Unless there's some code that reproduces the error using standard Node.js requests, I'm afraid I can't help.
I was able to reproduce the same issue using requests:
exports.awsGETRequest = async (url, apiEndPoint, params) => {
let role = await sts.getRole()
var opts = { host: url, path: apiEndPoint + params, service: prop.serviceName, region: prop.awsRegion }
aws4.sign(opts, { accessKeyId: role.Credentials.AccessKeyId, secretAccessKey: role.Credentials.SecretAccessKey, sessionToken: role.Credentials.SessionToken })
var options = {
'method': 'GET',
'url': url + apiEndPoint + params,
'headers': {
'X-Amz-Security-Token': role.Credentials.SessionToken,
'X-Amz-Date': opts.headers["X-Amz-Date"],
'Authorization': opts.headers.Authorization
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
return response
});
};
I think you're using the 3rd party request
module – not the standard Node.js https module.
Please see the README for usage:
https://github.com/mhart/aws4#example
Try something like this for debugging purposes:
const https = require('https')
exports.awsGETRequest = async (url, apiEndPoint, params) => {
let role = await sts.getRole()
var opts = { host: url, path: apiEndPoint + params, service: prop.serviceName, region: prop.awsRegion }
aws4.sign(opts, { accessKeyId: role.Credentials.AccessKeyId, secretAccessKey: role.Credentials.SecretAccessKey, sessionToken: role.Credentials.SessionToken })
https.request(opts, function(res) { res.pipe(process.stdout) }).end(opts.body || '')
}
Same result with https. I'm starting to think something on the backend may be causing this since it used to work.
exports.awsGETRequest = async (host, apiEndPoint, params) => {
let role = await sts.getRole()
var opts = { host: host, path: apiEndPoint + params, service: prop.serviceName, region: prop.awsRegion }
aws4.sign(opts, { accessKeyId: role.Credentials.AccessKeyId, secretAccessKey: role.Credentials.SecretAccessKey, sessionToken: role.Credentials.SessionToken })
var options = {
'method': 'GET',
'host': host,
'path': apiEndPoint + params,
'headers': {
'X-Amz-Security-Token': role.Credentials.SessionToken,
'X-Amz-Date': opts.headers["X-Amz-Date"],
'Authorization': opts.headers.Authorization
}
};
var req = https.request(options, (res) => {
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error)
})
req.end()
};
I'm not sure why you keep redeclaring the options – aws4 signs standard Node.js options. Just do this:
exports.awsGETRequest = async (host, apiEndPoint, params) => {
let role = await sts.getRole()
var opts = { host: host, path: apiEndPoint + params, service: prop.serviceName, region: prop.awsRegion }
aws4.sign(opts, { accessKeyId: role.Credentials.AccessKeyId, secretAccessKey: role.Credentials.SecretAccessKey, sessionToken: role.Credentials.SessionToken })
var req = https.request(opts, (res) => {
res.on('data', (d) => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error)
})
req.end()
};
Maybe you need to print out some debugging info.
Like apiEndPoint
and params
– do they make sense to join together as a path with no separator?
That would mean that apiEndPoint
begins with a /
and params
is a string that begins with a ?
correct?
I have debugged that and the url is structured correctly. I do get this back in the response header though.
statusCode: 403
headers: {
date: 'Mon, 28 Dec 2020 01:17:23 GMT',
'content-type': 'application/json',
'content-length': '1017',
connection: 'close',
'x-amzn-requestid': <guid>,
'x-amzn-errortype': 'InvalidSignatureException',
'x-amz-apigw-id': <id>
}
The reason I have opts and options is because I need opts.headers.Authorization in options, which has my header and GET as method.
Those options will already be set in opts
– as the README says, this library signs Node.js options – so you don't need to modify or redeclare anything, you can you them directly with https.request
Unless there's something reproducible here I'm going to need to close this
I ran into the same issue (request signature we calculated does not match the signature you provided), and it was also when I was trying to sign a request to invoke a method on an API Gateway, so I suspect we had the same problem:
I had the host
set as the API Gateway endpoint with the stage name at the end:
abcde1234.execute-api.ca-central-1.amazonaws.com/dev
however it must be using the endpoint name only, i.e.
abcde1234.execute-api.ca-central-1.amazonaws.com
Then in the path you prepend the stage name to the method you are trying to access, like /dev/methodname
Hope this fixes it for you!
Did you find a solution on this? I'm having the same issue. I can use the keys generated in my code on Postman without any issue. Am I missing anything??
Here is my code:
let options = {
host: 'sellingpartnerapi-na.amazon.com',
method: 'GET',
path: '/vendors/orders/v1/purchaseOrders?createdAfter=2021-01-12',
region: 'us-east-1',
service: 'execute-api',
headers: {
'x-amz-access-token': fetchResponse.access_token
}
}
let signedRequest = aws4.sign(options, {
accessKeyId: role.Credentials.AccessKeyId,
secretAccessKey: role.Credentials.SecretAccessKey,
sessionToken: role.Credentials.SessionToken
})
signedRequest.url = 'https://sellingpartnerapi-na.amazon.com/vendor/orders/v1/purchaseOrders?createdAfter=2021-01-12'
request(signedRequest, function(err, res, body) {
console.dir(res.body);
});
I have tried using request, node-fetch, axios, and all of them having the same error.
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.
Oddly enough, when I used https, it returned a different error:
{
"errors": [
{
"message": "Access to requested resource is denied.",
"code": "Unauthorized",
"details": ""
}
]
}
@leoliang92 please see other threads about getting this working with sellingpartnerapi:
https://github.com/mhart/aws4/issues/113#issuecomment-639291510 https://github.com/mhart/aws4/issues/121#issuecomment-730849155
Closing this out
With the following code I see this error:
I grabbed the keys generated and tried them in postman and the request works so something must be off with the signature. Am I using this incorrectly?