mhart / aws4

Signs and prepares Node.js requests using AWS Signature Version 4
MIT License
703 stars 176 forks source link

Issue with Websocket API on API Gateway - 403 error #133

Closed sirkupkup closed 3 years ago

sirkupkup commented 3 years ago

Hi,

I'm trying to sign a request that I'm sending to an endpoint on API Gateway. I'm sending this from a lambda that handles websocket connections.

Here is the data-part of the response I am getting when sending the request through axios (redacted parts of this as I dont know what could be compromising):

data: {
      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.\n' +
        '\n' +
        'The Canonical String for this request should have been\n' +
        "'POST\n" +
        '/wss/%40connections\n' +
        '\n' +
        'content-length:16\n' +
        'content-type:application/json\n' +
        'host:[redacted].execute-api.us-east-2.amazonaws.com\n' +
        'x-amz-date:20210603T142836Z\n' +
        'x-amz-security-token:IQoJb3JpF6xJRbN9DcBSTXz2u/5onmVzKdqtP3mGpGLtMlVpO3eakgQ6LDk0tRyxHrwWyIVLx4wEkH94xpay4qg07uEPk6tneP1NbMdrHAihqX0fjsVcdkPWkMMQYhO0E73QsbtEd3Bdt7pLqPwnESjBG9Egz622YVtIfpZf38nE[redacted-part]Addko2zVOhuuaERCudq8W9UHNBUu4DnfR8i+AT87OElMhUViIfTpKHE9Ok5TUrqGNKAbvJrnQQm+f+a/tXFJutkwZa6Z8M0zr5BVmDVOxWN2iFvu942K7QA21QYAcXkNtASrJiCyiLDtsKqeDo4PzQEVW3zVITZIjys9akVjXidTVHnkw8ZPd9KNwxLdRwnZR/snpqVzxReMtbw=\n' +
        '\n' +
        'content-length;content-type;host;x-amz-date;x-amz-security-token\n' +
        "c955e57777ec0d73639[redacted-part]656add3908f97'\n" +
        '\n' +
        'The String-to-Sign should have been\n' +
        "'AWS4-HMAC-SHA256\n" +
        '20210603T142836Z\n' +
        '20210603/us-east-2/execute-api/aws4_request\n' +
        "314957834cd46d17825[redacted-part]63bb8585946f60dad'\n"
    }

Here is the code I'm using to create and send the request:

const data = {'success': true};
                const request = {
                    host: process.env.AWS_API_GATEWAY_ENDPOINT,
                    method: 'POST',
                    url: `https://${process.env.AWS_API_GATEWAY_ENDPOINT}/wss/@connections`, // also tried replacing @ with %40
                    path: `/${connectionId}`,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(data),
                    data,
                }

                const signedRequest = aws4.sign(request,
                    {
                        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
                        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
                        sessionToken: process.env.AWS_SESSION_TOKEN
                    })

                // delete signedRequest.headers['Host']
                const response = await axios(signedRequest)
                console.log( 'axios resp: ', response );

Thanks for any help!

mhart commented 3 years ago

I can't really help with axios I'm afraid – I don't know it at all. aws4 needs a host and a path (it doesn't use url) – you sure the path is correct here? Shouldn't it be /wss/@connections/${connectionId}?

sirkupkup commented 3 years ago

Thank you, you are right!

I actually managed to solve it jumping it a bit to your code (unfortunately saw your answer only afterwards). You are correct - url and body is for axios.

For anybody else out there, here is the finished version. Hope it helps somebody:

                const data = {'success': true};
                const request = {
                    host: process.env.AWS_API_GATEWAY_ENDPOINT,
                    method: 'POST',
                    url: `https://${process.env.AWS_API_GATEWAY_ENDPOINT}/wss/@connections/${connectionId}`,
                    path: `/wss/@connections/${connectionId}`,
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(data),
                    data,
                }

                const signedRequest = aws4.sign(request,
                    {
                        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
                        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
                        sessionToken: process.env.AWS_SESSION_TOKEN
                    })

                delete signedRequest.headers['Host']; // It otherwise messes with axios
                const response = await axios(signedRequest);
amouly commented 3 years ago

@sirkupersmidt did you make it work with API GW v1 or v2? I'm also getting 403 with the same code as you posted. Thanks.

sirkupkup commented 3 years ago

@amouly here is a link to a post i wrote that explains exactly how to work this thing out :)

https://stackoverflow.com/a/67824275/10313866

Good luck!

javeedrahman commented 2 years ago

Hi @mhart @sirkupersmidt ,

I have stuck with same error but with SQS

Here is my code

`let requestSqs = { host: "sqs.us-east-1.amazonaws.com", region: "us-east-1", service: "sqs", path: "/?Action=SendMessage", method: "GET", headers: { "Content-Type": "application/x-www-form-urlencoded", Host: "sqs.us-east-1.amazonaws.com", "X-Amz-Date": amzDate, }, };

let signedRequest = aws4.sign(requestSqs, { accessKeyId: "*****", secretAccessKey: "****", });

delete signedRequest.headers["Host"]; delete signedRequest.headers["Content-Length"];

const response = await fetch( https://sqs.us-east-1.amazonaws.com/*************/sqsname/?Action=SendMessage&MessageBody=javedrahman, signedRequest ); ` But this was working fine in postman. Looking for your reply

Thanks in advance

sirkupkup commented 2 years ago

Need some more info - what error are you getting for example? Is it possible a perms issue?