How can I decode and verify the signature of an AWS SigV4 request using node.js

6.6k Views Asked by At

My setup

I have a request to an api gateway endpoint which is signed using AWS Sigv4

The headers for the request to look like this

{
  "host": "localhost:3100",
  "connection": "keep-alive",
  "content-length": "78",
  "sec-ch-ua": "\"Google Chrome\";v=\"87\", \" Not;A Brand\";v=\"99\", \"Chromium\";v=\"87\"",
  "sec-ch-ua-mobile": "?0",
  "authorization": "AWS4-HMAC-SHA256 Credential=ASIA2DP7X6SIMAVM65UJ/20201223/ap-southeast-1/execute-api/aws4_request, SignedHeaders=accept;content-length;content-type;host;x-amz-date;x-amz-security-token, Signature=edc00f240870f9f1e0e8fc66f1ef98c3dac4bfbc1f1a8ab36dacc4b68b32b2a3",
  "content-type": "application/json",
  "accept": "*/*",
  "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
  "x-amz-security-token": "IQoJb3JpZ2luX2VjEAwaDmFwLXNvdXRoZWFzdC0xIkYwRAIgF+XX6C0F+P5XgVewIKXVvPt+ahQkHVOj6rHPC5nzAaQCIEq8ldeNs5vv1xvt76Fl+osd/cPZZTbjhrDCYJqeyy7mKqEGCLX//////////wEQABoMNjk0NzEwNDMyOTEyIgyGVcLvP4mHok3ZyIQq9QUBEp/j5AeqnRV6D3IEl4dCiLUhdJaTLX+8JdBinM3eiTSyAlOQUucsNStudFc4qMVPztBnBFpx7diDDjh9FqM3Wx+Xs+qTfWIP6s3dgZioPh+M526e7SUiI4aoSjBhmDzrzAgoVvI5azbNCl3nvfONC7uvGzQC8zZc/W+4/1icnny6vuaooZnQUZn3t8GHasfepVJ1zcDjUUN+76cw+txiuBB7mzoFhnhfvEEDdN1aHgZoMUAYWUhvlAq+gLZa8CdwX4mThkpZodf6t04n8QLkqRmUb1KQd0sG4Vux6OzXbO+j3mtgasbHEtxS7kM2ZY++ZYG9A5tyNhXSxfjtk6HkpaEi5WOHTfH94fk8/VG5gmPj19z48uBoxa1ct4jThsrg81kmcrC00gbNwrXqHh4l0/4ufAvfuO8MWxvHqkXks6U2WU4rzu0QVhSg2cIxHP7mH1FQyYAA31D1Pz23QpYB45o6TppSa/j8VmzsYZ8NCn+fwuVeP7RCxzic0YWdbKc1d5Ts0ID9M8Pg+d/0CgJSVfrlBD0QOytIjLbhvFAwkSJeiDk/Mw6rI4nRNXtQy2EgoUsspaTo4buePIr/eflEUm1dpAEu33CX0TzUhszFmFmq8lLFlQUEl1VKVvw6Jdz0x8uhX2vdT33ixnpavQpWXA3eKu4RVuzSIyvat+OhQX7ywIzpyhbcSsIQR+zq62RfM7RDd6iNG41tzBPNk/dQpJhBmMJdqQE8fX4Uw2PgJaEGEyyFRpvYMT2OtMG+2CUNj6FiyTSCsa00H+ZKR/knXRXhnyHmOdDsQDbbgPjdr5wougoQTxx5BPk9/6lOSXdsGhg9oT1FkOsGOIZgoaaRA+cRvMAzMERQd4hAboeqGmuoP+7dAwgE4QcwChx+vK3kdCWQ8MRfeYs3MQTWvd4R9fx0VOeCfza0nYBEqqsfBs2YBCDYPHJl9nNKU8xrspn1kDnQ03m3P1hwM81FqmuNXpZaTiUnDjHj+sWSDE0e/6f023UzMNWKi/8FOogCS99u4WOLBdmKnHFptz2yH8fM9ewGniGeSE6J40rRxRmGKjfoBiipYeY2XQgRE5TLzxO5RlqRxgcYQ+Y4fddYR66hjQSi0YJPe30aPNgXNolvmabeh5mi17NEeeNklauHeuM1Te81N8dnZSYiubxtpiwbb3EyTZhmM/mi3w22hfQcz+ufrtwZYjBlzCYc+szynuRS5mbS4HnwPhIgeyZyrpPWW/mDwXjWPnV437Cp2654UZkaWi0/HTHuwcnNs0JEOZ81cq9LXwqCUYqyjHzWf25JSTn06q/kVVhW8RZgULDabEcvl3DKgG6dau+dK2sTbVwmOKGHw7lsx2RrCmbA1gqjscOb9zt3",
  "x-amz-date": "20201223T041933Z",
  "origin": "http://localhost:3000",
  "sec-fetch-site": "same-site",
  "sec-fetch-mode": "cors",
  "sec-fetch-dest": "empty",
  "referer": "http://localhost:3000/",
  "accept-encoding": "gzip, deflate, br",
  "accept-language": "en,en-US;q=0.9"
}

When I run add an IAM authoriser to my api endpoint I see that this signature is verified by AWS as follows

{
  "identity": {
    "cognitoIdentityPoolId": "ap-southeast-1:776d43fa-1a9a-4911-b06c-8a6580ae1bcd",
    "accountId": "694710432912",
    "cognitoIdentityId": "ap-southeast-1:053f57cd-6a21-486d-b4b5-5fd27e17420b",
    "caller": "AROA2DP7X6SILQ2KYY6MJ:CognitoIdentityCredentials",
    "sourceIp": "103.252.202.229",
    "principalOrgId": null,
    "accessKey": "ASIA2DP7X6SIGUWRHSN3",
    "cognitoAuthenticationType": "unauthenticated",
    "cognitoAuthenticationProvider": null,
    "userArn": "arn:aws:sts::694710432912:assumed-role/Cognito_MyPoolUnauth_Role/CognitoIdentityCredentials",
    "userAgent": "PostmanRuntime/7.26.8",
    "user": "AROA2DP7X6SILQ2KYY6MJ:CognitoIdentityCredentials"
  }
}

My Question

Can I verify the signature myself using nodejs (typescript)? Any examples of a script that decodes such headers to produce such an identity would be awesome.

Why I want to do this

For mocking my api along with the authorization logic locally. Also I may want to experiment with a custom lambda authorizer instead of the default AWS IAM Authorizer in my gateway (to give me some more control over my authorization logic).

1

There are 1 best solutions below

3
On

The response to your question is a little bit complicated. In short: yes, you can! The longer answer is: technically is possible, but in practice, it is not!

The signature process is very straight forward. It is entirely documented here. To validate the signature (the authorization header) you just need to repeat the process and check if the result is equal to the signature in the header. The problem is: to repeat the process you must have the ACCESS_KEY_ID and the SECRET_ACCESS_KEY. You can make it work with an AWS principal access key and secret. But you're using Cognito Identity Pools and, in that case, you'll never have access to the SECRET_ACCESS_KEY.

It will work if your intention is just mock your logic locally. But if you allow me, I would suggest to do not try this at home. If your purpose is just to mock the logic, the easy path is to bypass the API Gateway and send a mock object respecting the API Gateway Event format directly to the function with the "decoded/mocked" identity token.

Another thing: If you move to Custom Authorizer using Cognito Identity Pools (what you are using) you will need to perform a lot of tasks manually, like validate the signature and decode the identity token (I believe it is the content of x-amz-security-token). In summary, you will need to do manually all the tasks that are performed by the IAM Authorizer.