2

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).

Simon Verhoeven
  • 347
  • 4
  • 16

1 Answers1

3

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.

Gustavo Tavares
  • 2,579
  • 15
  • 29
  • Hi. Thanks for your reply. Yes I had considered this "send a mock object respecting the API Gateway Event format directly to the function with the "decoded/mocked" identity token." But my problem is that different roles will have different levels authentication and this logic is in my lambda its-self (as in behind the authorizer and the decoded role). (e.g users with role x can editBooks role y can only addBooks). Is there anyway I can decode the authorization headers to see what role is being claimed? I don't need to verify the claim only mock the correct identity for each request. – Simon Verhoeven Dec 23 '20 at 18:58
  • There are some things that are not clear in your solution. I understood that you are using Cognito Identity Pools integrated with Cognito User Pools. If that is the case, I'm almost sure that you won't get the role information in the token. To have the roles in the token I believe you'll need to use Cognito Authorizer. But with that, you'll lose the ability to have unauthenticated users. – Gustavo Tavares Dec 24 '20 at 16:02
  • If you use the Cognito Authorizer the roles will be present inside the identity object when the event is passed to the Lambda Function. If you don't use the Cognito Authorizer (to be able to have unauthenticated calls, like in your example), my guess is that you will need to get the userId and using `identity. cognitoAuthenticationProvider` go to the UserPool and get the roles for the authenticated user using `identity.caller` or `identity/user` (or in the worst case you'll need to query the Id Pool to discover the UserPool id). – Gustavo Tavares Dec 24 '20 at 16:09