Is it possible to add an HTTP header from AWS Custom Auth on API gateway? - amazon-web-services

I am using Custom Auth on AWS API Gateway, but I would like to add an extra HTTP header depending on the result. Does anyone know if this is possible, or how to do it. If it is not, is there an idea of if or when this will be possible?
Many thanks.

We recently added support for this. Docs should be up soon.
Now you can return an object like this from the authorizer function:
{
"principalId": "xxxxxxxx", // The principal user identification associated with the token send by the client.
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}
]
},
"context" : {
"key" : "value",
"numKey" : 1,
"boolKey" : true
}
}
Arrays and objects aren't allowed, only string/number/boolean as valid JSON. The root key must be named context.
You can access those values in the request $context like so:
$context.authorizer.key -> value
$context.authorizer.numKey -> 1
$context.authorizer.boolKey -> true
So to answer your question, you wont' be able to conditionally add the header, but you could set the header value to $context.authorizer.yourKey and if yourKey isn't set in the authorizer response, the header value would be blank (but the header would still be sent).
Edit:
Docs are live http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html

I was able to get this working after a day of pulling my hair out and hopefully I can save someone from that. It adds a bit more to Jacks' response.
Basically, you can dynamically add headers by using Body Mapping Templates
Create and Authorizer Lambda (you can use the authorizer blueprint lambda to get started), do your business logic to create the AuthPolicy and populate the context object with the key/values.
On the API Gateway, select the resource, click on Method Request and set the Auth to your Authorizer lambda
Open Method Execution, select the Integration type and make sure to unselect Use Lambda Proxy integration (if your request points to a lambda)
Add a Body Mapping Template - create one from the template and this is where you have access to $context.authorizer.key
Add the following to your template (just under "body-json" : $input.json('$')," is fine)
"headers": {
"key-header" : "$util.escapeJavaScript($context.authorizer.key)",
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))"
#if($foreach.hasNext),#end
#end },
This will add a new header called "key-header" and also forward all the original headers allowing you to append additional information like user_id, user_role, etc to the upstream service.

I tried Emanuel Canha's answer above, but that no longer seems to work. The way I got it to work yesterday (5 June 2019) is to
Create the lambda
Add the lambda under Method Request as your authorizer
Open Integration Request
Open HTTP Headers
Add a header with the name you want (key-header in the above example)
Use context.authorizer.yourKey as the mapped from entry. (Note that you do not use the $ in this field.)

You can only get PrincipalId from authorizer result, in your integration request, you can map a header value using context.authorizer.principalId

These answers seem to be specific to HTTP API Gateways. We're trying to user RestAPI API gateway which would work with both Lambdas (that take pricnipalID as part of the event object forwarded by Lambda Authorizer) but also, we would like to Authorize non-Lambda endpoints (deployed in private VPC and accessed via Network Load Balancer) so we want to put the principalId into HTTP headers the endpoint would parse.

Related

AWS API Gateway integration request Http headers not being passed to lambda

This is similar to this post API-Gateway Integration Request HTTP Header not mapping query string to header but I can't see any answer for it and all of the answers are not related to what the question is intended.
I am trying to add the integration request parameters in API Gateway, but whenever I set 'Http Headers' as shown here and 'Mapping template' as passthrough, I could not see that header when I log inside the lambda.
Also in the integration response I cannot reference it inside the integration header response parameters. The "integration.response.header.cokers" will be returned blank when I invoke the API. This is how I configured the integration response
Ultimately the solution here is to implement a lambda proxy integration.
A proxy integration in API Gateway tells API Gateway to simply forward all headers to the integration for processing, which means you will see all of those values in your lambda function.
NOTE: Lambda has to return a specific response format back to API Gateway if it's a proxy integration. Ultimately, the response should be something like:
{
'statusCode': 200,
'headers': {
'echo-proxy': True
},
'body': 'some payload'
}
What you are trying to do right now is map everything manually, which is a deprecated approach and usually you don't want to do that unless you absolutely have to because it's kind of a pain.
If you have to map the headers manually start by mapping them in the the method request so it can carry on to the next step and so on. Basically like this:
Method Request -> Maps variable to Integration Request -> Maps variable to Body Mapping Template -> Maps variable to actual request header
What you have in your screenshot for the Integration Request -> HTTP Headers is:
Name: cokers
Mapped from: 'blah'
However, "mapped from" should look something like "method.request.header.coker" which is a standardized path (meaning to get the value from the Method Request Header field with name "coker").
Once you have added the coker header to the Method Request, and the Integration Request HTTP Headers are mapped correctly, you have to implement a mapping template. Set the content-type to application/json with passthrough set to "When there are no templates defined(recommended)" and a simple mapping template:
{
"headers": {
"coker" : "$input.params('coker')"
}
}
That is the way my API is setup and it returns the following to me because I had my lambda function return the event as a json object back to API GW:
{"body": "{\"headers\": {\"coker\": \"mapped\"}}", "statusCode": 200}
NOTE: the value of my header "coker" in the request on the client side is "mapped"
UPDATED ANSWER
To map the original "coker" header to "coker2" (or any other name you want to give it) you simply set the name of the header in your mapping template like so:
{
"headers": {
"coker2" : "$input.params('coker')"
}
}
Then edit your lambda function to return "coker2" header and you should get a response like this:
{"body": "{\"headers\": {\"coker2\": \"mapped\"}}", "statusCode": 200}

How can an aws lambda know what endpoint called it from API Gateway?

If two different endpoints use one lambda, how can the lambda know about the parts of the URL path?
How can one lambda know it was called from /zips vs /zip?zip_code=02140 ?
I can use event["queryStringParameters"]['zip_Code'] to get the URLs query string - /zip?zip_code=02140 - from within the lambda,
but how can I know if I am called from the /zips endpoint?
I tried using event["pathStringParameters"]['zips'] which I created a test event for but that didn't work, not recognized.
I can use one lambda per specific resource but I'd like to also know other approaches and how those that use the same endpoint can have their path revealed.
If I am following what you're asking for, namely that you have one Lambda function servicing two API Gateway endpoints, then I think you have two options:
Use the path parameter
Set a custom header and check that in headers
From the AWS documentation:
In Lambda proxy integration, API Gateway maps the entire client request to the input event parameter of the backend Lambda function as follows:
So given this HTTP request:
POST /testStage/hello/world?name=me HTTP/1.1
Host: gy415nuibc.execute-api.us-east-1.amazonaws.com
Content-Type: application/json
headerName: headerValue
{
"a": 1
}
You'll have available:
"message": "Hello me!",
"input": {
"resource": "/{proxy+}",
"path": "/hello/world",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
...
Here both path and headers will serve your need.
Personally, I would recommend setting a custom header. That way, no matter if your API routing changes, your Lambda will still pick it up.
You can get the path that invoked your Lambda in the event object, under event["requestContext"]["path"]
You can see more details on what the event object contains in the documentation for using AWS Lambda with API Gateway

Using AWS API Gateway to POST email via AWS SES

I'm setting up an API from an App which allows the App user to contact someone for follow-up, but hides the email address from the sender (and allows it to be changed without a new release of the App).
I've managed to setup an AWS API Gateway GET method which sends email directly via SES
https://...aws.com/em/send/en?body=The+email+is+here
Using a path override of
Action=SendEmail
&Source=source%40mydomain.org
&Destination.ToAddresses.member.1=follow.up%40anydomain.com
&Message.Subject.Data=Request+For+Followup
&Message.Body.Text.Data={body}
I would much prefer to use a POST method - but am really struggling to work out what should go in the Action parameter and how to create the mapping template - or even if it is possible to create an application/x-www-form-urlencoded request without having to resort to a lambda function - although the AWS documentation does include a $util.urlEncode() function.
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Edit:
I am trying to use a POST method to AWS Gateway and a POST to SES with the contents in the request body, but it is just not clear to me how to create the mapping template and I can't find any examples for SES.
Testing it with the mapping template (as per the example in the documentation)
Source=source%40mydomain.org
&Destination.ToAddresses.member.1=follow.up%40anydomain.com
&Message.Subject.Data=Request+For+Followup
&Message.Body.Text.Data=The+message+goes+here
And a content type application/x-www-form-urlencoded AWS gateway gives the error:
{
"message": "Unsupported Media Type"
}
If I use a mapping template
{
"Action":"SendEmail",
"Source":"source#mydomain.org",
"Destination":{
"ToAddresses":{
"member":["follow.up#anydomain.com"]
}
},
"Message":{
"Subject": {
"Data":"Request For Followup"
},
"Body":{
"Text":{
"Data":"The message goes here"
}
}
}
}
and a content type of application/json AWS gateway gives the error
{
"Output": {
"__type": "com.amazon.coral.service#UnknownOperationException",
"message": null
},
"Version": "1.0"
}
Your mapping template configuration must be in a POST format, not in the JSON, since SES service doesn't support json.
For example, this config has worked out for me:
Action=SendEmail&
Message.Body.Text.Data=Some+text&
Message.Subject.Data=New+message&
ReturnPath=some#mail.io
&Destination.ToAddresses.member.1=some#destination.com
&Source=your#sender.email
Also, your path override must be SendEmail only for the case above

Using AWS API Gateway to build a patch method

I am using API Gateway to build a patch method.
In then Integration Request - Mapping Template i added:
{ "id": "$input.params('subscription-id')",
"env": "$stageVariables['env']",
"street": $input.json('street'),
"address_name": $input.json('address_name'),
"payment_day": $input.json('payment_day'),
}
As a patch http method, the user's API is not required to pass all the parameters.
So if the user doesn't pass, for e.g. payment_day, the field is going to be ''. The '' can be a valid value field. So i have two options:
Put a NULL value on the payment_day field.
Remove the payment_day from JSON request.
Is it possible to do this on API Gateway Integration Request -Mapping Template? Does anyone has a workaround?
You can use Velocity Conditionals to only output optional values if present.
Alternately, as mentioned in comments, you can just pass the entire JSON body using $input.json('$') and handle the presence or lack there of inside your Lambda function.

API Gateway CORS Issue

So I have CORS enabled going through the basic setup given by AWS Gateway. However for this API I need to allow Control Origins for all requests and allow credentials.
Here is what it looks like
The issue as you may have guessed is this setup is not allowed by CORS, you can not have a wildcard for Origin and have credentials as true. Normally the work around for this is to just grab the requesting domain and add it into the Origin Header. Which is more or less what I want to do. But I don't know how to get that information and add it as a mapping value. Where does API Gateway store that information and how do i get it?
UPDATE:
I have to pass through HTTP Header Host to my Lambda Function which I should have mentioned earlier, I have tried implementing the Answer below but I cannot access the header to pass it to the Lambda function using the instructions provided. Any more assistance with this is greatly appreciated.
Okay After hours of research and finding bits of information across the internet I have a solution and hopefully it is useful for other people.
To pass an HTTP Header that is not a default value provided by AWS API Gateway and then access that data via a Lambda Function and return that data in the Response Header follow the steps below
In "Method Request" go to "HTTP Request Headers" and add your desired header to capture. ie. if we want to get the host value of the API url you can enter "Host" here. if you want to get the website host of the caller use "Origin"
In "Integration Request" go to mapping templates and create a new template if an "application/json" does not exist, if it does just update it.
This is the important part, pass the header value you set in step 1. To do that write something similar to the following in the Template Box.
{
"origin" : "$input.params().header.Origin",
"host" : "$input.params().header.Host"
}
You can also pass in any url parameters you have defined in the same JSON.
Access the data from Lambda, The integration request passed the information into the "Event" parameter if using Node as the Lambda Backend code. to retrieve the value of any header just use the following within your handler.
event.origin;
When sending back your response from Lambda to API Gateway, it is best to format the response in JSON. Something similar to this.
{
"origin" : event.origin,
"host" : event.host,
"nonHeaderOutput" : "Hello World"
}
In "Integration Response" go to "Header Mappings", if the header you need is not listed you may add it in "Method Response" and it will then appear here. For this example I used "Access-Control-Allow-Origin" and edited the Mapping Value to be integration.response.body.origin
now go to "Mapping Templates and select the content type you want to use, and then edit the template to access the non header responses by adding this to the Template Box
$input.path("$.nonHeaderOutput")
That is it now the header that was sent to the API can be used in your method Response.
Jurgen from API Gateway here.
Thanks for reporting this issue. There is currently no simple way to set it up via the "Enable CORS" feature in the console. However, we are looking into it to improve the user experience for this use case.
As a potential workaround, you could passthrough the Origin header from the client to your backend and parse / create the value for the Access-Control-Allow-Origin header there. Then you would map the Header in the Integration Response from 'integration.response.header.Access-Control-Allow-Origin' to Access-Control-Allow-Origin and return it to the client.
Hope this helps.
Best,
Jurgen