Webex Teams Webhook with API Gateway and Lambda - amazon-web-services

I am using a fairly standard pattern of a Webhook with the called endpoint provided by AWS API Gateway and a backend Lambda.
Webex Teams webhooks allow you to provide a secret which is used to sign the outgoing payload with the resulting hash sent in the 'X-Spark-Signature' header.
I create a webhook and receive the event payload in my Lambda but the hashes do not match. Below is my example code:
def validate(key, raw):
hashed = hmac.new(key, raw, hashlib.sha1)
print(hashed.hexdigest())
return hashed.hexdigest()
key = bytes('somecazYs3Cret', 'UTF-8')
raw = bytes(event['body'], 'UTF-8')
signature = event['headers']['X-Spark-Signature']
if validate(key, raw) == signature:
print('AUTHORIZED')
else:
print('REJECTED')
In API Gateway I am using a Mapping Template as described here to pass the request headers through to my Lambda: https://aws.amazon.com/premiumsupport/knowledge-center/custom-headers-api-gateway-lambda/
When the request payload arrives, all fields including the body are already loaded as a python type dict. so I am trying to serialise the body back to a string to check the hash.
Any help?

This turned out to be the way API Gateway was passing the request payload through to Lambda. Instead of the "Mapping Template" I had to enable the "Use Lambda Proxy integration" feature which passes the original body JSON through as a string.
After enabling this and removing the json.dumps() parts of my code, the hashes validate ok.

Related

Is there any way to validate path parameters in AWS API Gateway?

I'm using AWS API Gateway to create an api.
I have the following path for an API: /users/{id}
Is there a way to validate the existence of id in the API request made and maybe its type in API Gateway before it reaches the Lambda integration? I understand API Gateway supports validating request body, query params and headers, but I can't see any option for path parameters, does API Gateway not support that?
I'm going through the documentation and I can't seem to find something clear on that.
API Gateway can check if a path parameter exists or not. It can check if path includes any "id", but it cannot for example check what is it’s regex pattern. Such validation should be made on a client side. Either the API Gateway path follows the defined pattern, in which case it can route you to an appropriate resource, or not. It can however ensure that request parameters are present and non-blank.
The AWS documentation states that API Gateway can perform the basic validation:
API Gateway can perform the basic validation. This enables you, the
API developer, to focus on app-specific deep validation in the
backend. For the basic validation, API Gateway verifies either or both of the following conditions:
The required request parameters in the URI, query string, and headers of an incoming request are included and non-blank.
The applicable request payload adheres to the configured JSON schema request model of the method.
To enable basic validation, you specify validation rules in a request validator, add the validator to the API's map of request validators, and assign the validator to individual API methods.
Note that comparison with JSON schema request model refers to the request payload, and not to request parameters. In the documentation you can find guidance on how to enable request validation in API Gateway:
by importing OpenAPI
definition
using the API Gateway REST
API
using AWS
console
Also, follow this blog post which explains how to set up request parameters validation. Mind however, that the basic validation ensures that the request parameters are present and non-blank. More advanced validation, like checking regex pattern or type, is not possible to my knowledge.

Headers in API Gateway Authorization are not been read

I've implemented a Lambda Function to the authorization and i'm getting errors in my test: the headers are empty. I could check that printing the headers in the console (Cloudwatch).
This is the beginning of my handler:
public class Authorizer implements RequestHandler<APIGatewayProxyRequestEvent, AuthorizerResponse> {
public AuthorizerResponse handleRequest(APIGatewayProxyRequestEvent request, Context context) {
Map<String, String> headers = request.getHeaders();
System.out.println("headers: " + headers);
String authorization = headers.get("Authorization");
...
And this is the result:
Talking about the Authorizer, I set up this way:
And finally, I have my Api Gateway set up this way:
Method Request
Integration Request
What do I have wrong?
Thanks in advance.
Let me answer your question here.
You are using a TOKEN type Lambda authorizer. This will pass the "Authorization" header value as a token. You can see the below sample Lambda event data -
{
type: 'TOKEN',
methodArn:'arn:aws:execute-api:$AWS_REGION:$ACCOUNT_ID:$API_ID/$STAGE/$RESOURCE/',
authorizationToken: 'allow'
}
You might have to modify your code accordingly to retrieve this token.
Alternatively, you can use a REQUEST-based Lambda authorizer function. This will send input to Lambda authorizer as a combination of headers, query string parameters, stageVariables, and $context variables. You can then use your existing code to retrieve the headers from the request.
As for setting a mapping template and "Authorization" header in Method Request and Integration Request, you don't need that unless you are planning to pass it to the integration Lambda function as well.

Use ApiGateway Authorizer to Validate Github Payload Signature (X-Hub-Signature)

I am currently working on a simple api to receive Github event payloads, and I want to validate that they are coming from the correct source. With this I am working to use the hmac signature in the requests header (generated by github using a secret provided by me). To validate the signature, the ApiGateway authorizer requires the signature (X-Hub-Signature), the secret used to generate the signature, and the body of the message. As far as I can tell, Api Gateway does not allow you to pass the body to an ApiGateway Authorizer. Does anyone know a way around this that does not require additional proxy lambdas and s3?
*Note: The requester is the Github Webhook service (not able to add body to header)
Basic ApiGateway Auth Docs:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
Here is how you do it.
Pass your content to Authorization header of your incoming request, It will get delivered to your custom Authorizer.
Grab the contents of the from the below attribute,
event.authorizationToken
where event is one of the parameters (1st) passed to lambda,
I currently encrypt and add all the info to that header and gets delivered to the Custom Authorizer lambda.
You can also access additional parameters as below in your custom Authorizer lambda:
var headers = event.headers;
var queryStringParameters = event.queryStringParameters;
var pathParameters = event.pathParameters;
var stageVariables = event.stageVariables;
var requestContext = event.requestContext;
Hope it helps.

AWS API authorizer include body

Is there a way to validate request in API Gateway based on its body? I need to calculate SHA1 hash of the body to validate the sender - Facebook messenger events... Is there a workaround for it?
ApiGateway does not support passing complete body to custom authorizer.
One option is to have two level of authentication - first just based on header/query parameter ( which api gateway support ) and enough to detect spoof senders. Second can be SHA1 hash based on complete body which you can implement in your backend

When using Amazon API Gateway, how do I get the API key used in the request from a Django backend?

Pretty self explanatory title. I'm using API Gateway in AWS, requiring an API key to access a backend written in Django (not using lambda). I need to know how to access the API key used in the request to keep track of who did what at the app level.
You can use mapping templates and get the API Key from the $context variable, it’s the apiKey property inside the identity object: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference
Create a mapping template for your requests and include the property in it. For example, if you wanted to include the entire request body + the API Key you would do this:
{
"body": $input.json('$'),
"apiKey": "$context.identity.apiKey"
}
Depending on how your backend application is built, you could send the API key to your application in a HTTP parameter (path, query string, or header) or in the request body. Please have a read through the docs on how to move data between the two systems.
Thanks,
Ryan
Here is how I finally made it work. At the top or bottom of the template, include this line.
#set($context.requestOverride.header.x-api-key = $context.identity.apiKey)
When your backend receives this request, the api key will be in the header x-api-key.
Here is a basic mapping template that just forwards the (json) body and the header.
$input.json("$")
#set($context.requestOverride.header.x-api-key = $context.identity.apiKey)
API Gateway uses the X-API-Key header, so I like for my backend to also use that. That way I can use the same testing commands with only the URL being different.