Closed max77p closed 4 years ago
I am getting the same error message when trying to use SES.
I can make it work however, when using region us-east-1
instead of eu-central-1
Using the AWS CLI both regions are working.
Here's some code to reproduce the issue:
const aws4 = require('aws4')
const request = require('request')
const querystring = require('querystring')
const credentials = {
accessKeyId: 'XXXXXXXXXX',
secretAccessKey: 'XXXXXXXXXX'
}
const fromEmail = 'some@email.com'
const toEmail = 'some@email.com'
const options = {
// Those DO NOT work:
uri: 'https://email.eu-central-1.amazonaws.com',
host: 'email.eu-central-1.amazonaws.com',
// Those DO work:
//uri: 'https://email.us-east-1.amazonaws.com',
//host: 'email.us-east-1.amazonaws.com',
headers: {
Connection: 'Keep-Alive',
},
service: 'ses',
body: querystring.stringify({
Action: 'SendEmail',
AWSAccessKeyId: credentials.accessKeyId,
Source: fromEmail,
'ReplyToAddresses.member.1': fromEmail,
'Destination.ToAddresses.member.1': toEmail,
'Message.Subject.Data': 'Test',
'Message.Subject.Charset': 'UTF-8',
'Message.Body.Text.Data': 'Test',
'Message.Body.Text.Charset': 'UTF-8',
})
}
const signedOpts = aws4.sign(options, credentials)
console.log({ signedOpts})
request(signedOpts, function (err, res) {
console.log(res.body)
})
I just figured, that not passing the host
option fixes it
If you don't pass all the following options, then aws4
tries to figure out which you mean: host
, region
, service
. If you're having errors, then you need to make sure all of these match
Closing this βΒ it seems like an axios issue (unless you can reproduce with the Node.js http/https module)
@mhart actually NOT passing the host option does not fix it. This just causes the Host
header to be set to the default region us-east-1
which means I am ending up using the wrong region π¬
As soon as I pass hostname
or host
to 'eu-central-1' I get the SignatureDoesNotMatch
error.
Right, but in your code it doesn't look like you're setting region
Just try setting service
(ses
) and region
(eu-central-1
) β aws4
should automatically figure out the host from those two and you won't need to supply it.
Okay, tried that. Only passing region and service, but same result.
And yeah, aws4
DOES figure out hostname automatically based on service
and region
.
But with region set to eu-central-1
the request results in the SignatureDoesNotMatch
error.
This is the signedOpts for eu-central-1
which does not work:
{ signedOpts:
{ region: 'eu-central-1',
headers:
{ Connection: 'Keep-Alive',
Host: 'email.eu-central-1.amazonaws.com:443',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Length': 297,
'X-Amz-Date': '20200414T195958Z',
Authorization:
'AWS4-HMAC-SHA256 Credential=XXXXXX/20200414/eu-central-1/ses/aws4_request, SignedHeaders=connection;content-length;content-type;host;x-amz-date, Signature=XXXXXX' },
port: 443,
method: 'POST',
service: 'ses',
body: 'β¦',
hostname: 'email.eu-central-1.amazonaws.com:443',
path: '/' } }
And the same for us-east-1
which does work:
{ signedOpts:
{ region: 'us-east-1',
headers:
{ Connection: 'Keep-Alive',
Host: 'email.us-east-1.amazonaws.com:443',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Length': 297,
'X-Amz-Date': '20200414T200253Z',
Authorization:
'AWS4-HMAC-SHA256 Credential=XXXXXX/20200414/us-east-1/ses/aws4_request, SignedHeaders=connection;content-length;content-type;host;x-amz-date, Signature=XXXXXX' },
port: 443,
method: 'POST',
service: 'ses',
body: 'β¦',
hostname: 'email.us-east-1.amazonaws.com:443',
path: '/' } }
And again, using the CLI works with both regions :(
I tried replacing all us-east-1
with eu-central-1
in aws4.js
to see if different fallbacks have any impact, but did not change anything. The host in the header is set correctly, just the signature is not valid.
How are you making your http requests? Are you using the Node.js http/https modules? Or are you using a third-party library? Please post the code you're using to make the request.
EDIT: nevermind about the host question, let's just figure out what code is being used to make the request
I was using the request
lib, but I get the same result using the https
lib.
const aws4 = require('aws4')
const https = require('https')
const querystring = require('querystring')
const credentials = {
accessKeyId: 'XXXXXXX',
secretAccessKey: 'XXXXXXX'
}
const fromEmail = 'mail@example.org'
const toEmail = 'mail@example.org'
const options = {
service: 'ses',
region: 'eu-central-1',
headers: {
Connection: 'Keep-Alive',
},
body: querystring.stringify({
Action: 'SendEmail',
AWSAccessKeyId: credentials.accessKeyId,
Source: fromEmail,
'ReplyToAddresses.member.1': fromEmail,
'Destination.ToAddresses.member.1': toEmail,
'Message.Subject.Data': 'Testxxx',
'Message.Subject.Charset': 'UTF-8',
'Message.Body.Text.Data': 'Test',
'Message.Body.Text.Charset': 'UTF-8',
})
}
const signedOpts = aws4.sign(options, credentials)
const req = https.request({
hostname: 'email.eu-central-1.amazonaws.com',
port: 443,
path: '/',
method: 'POST',
headers: signedOpts.headers
}, res => {
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
req.on('error', error => {
console.error(error)
})
req.write(signedOpts.body)
req.end()
Prints:
statusCode: 403
<ErrorResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<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.</Message>
</Error>
<RequestId>7d62a9c3-f1d2-4f26-bd42-90222b40cba9</RequestId>
</ErrorResponse>
Value of signedOpts
:
{ region: 'eu-central-1',
headers:
{ Connection: 'Keep-Alive',
Host: 'email.eu-central-1.amazonaws.com:443',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Length': 297,
'X-Amz-Date': '20200414T203457Z',
Authorization:
'AWS4-HMAC-SHA256 Credential=XXXXXXXX/20200414/eu-central-1/ses/aws4_request, SignedHeaders=connection;content-length;content-type;host;x-amz-date, Signature=3ee97a7149f5ca7f0274e2e20fb5c9363e70748cfdc2278e916de250b6e9b755' },
port: 443,
method: 'POST',
service: 'ses',
body: 'β¦',
hostname: 'email.eu-central-1.amazonaws.com:443',
path: '/'
}
You're only passing the headers. The README has an example of how to use this library.
const req = https.request(signedOpts)
(also, try removing the Connection: 'Keep-Alive'
)
.....it was the Keep-Alive
header! But why the hell does it work with region us-east-1
?? I am so puzzled.
Anyway, thank you very much for the support @mhart!π π
Not sureΒ β it might be that different regions have different versions of the signing code for verifying the signatures.
You can always try adding the Connection
header again after signing. Eg:
const signedOpts = aws4.sign(options, credentials)
signedOpts.headers.Connection = 'keep-alive'
const req = https.request(signedOpts)
Also, I've never seen it capitalized before β it might be expecting 'keep-alive'
(or, aws4
is signing it as 'Keep-Alive'
, but then Node.js is changing it to 'keep-alive'
before it sends, and so the server gets a mismatch)
I tried lowercase keep-alive
but this has no effect either. So I changed the code to add the header after signing. Thanks again for your help π
I am getting the above error..am I passsing the accesskey and secretaccesskey correctlhy to sign this request in axios?