AWS lambda authorizer, Custom Context values in REST API - amazon-web-services

How could I retrieve the output of custom authorizer in lambda integration?
for example, lets assume below is my swagger file with aws api gateway integration, lambda authorizer output and AwsProxyHttpServletRequest;
A question in SO here AWS API Gateway with Lambda Authorizer says it works, but not for me.
REST API
openapi: 3.0.0
info:
title: Sample Event
version: 1.0.0
# Enable request validator. See doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-validation-sample-api-swagger.html
x-amazon-apigateway-request-validators:
all:
validateRequestBody: true
validateRequestParameters: true
x-amazon-apigateway-request-validator: all
x-amazon-apigateway-gateway-responses:
# Provide more detailed error message for bad request body errors. See doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-gateway-responses.html
BAD_REQUEST_BODY:
responseTemplates:
application/json: '{"errorCode": "BadRequestBody", "message": "$context.error.validationErrorString"}'
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
DEFAULT_4XX:
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
responseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
paths:
/events:
post:
operationId: CreateEvent
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/CreateEventInput"
required: true
responses:
"201":
description: "Successfully Created an event."
content:
application/json:
schema:
$ref: "#/components/schemas/Event"
"400":
description: "Bad Request Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/BadRequestException"
"401":
description: "Unauthorized Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/UnauthorizedException"
"409":
description: "Conflict Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/ConflictException"
"429":
description: "Too Many Requests Exception"
content:
application/json:
schema:
$ref: "#/components/schemas/TooManyRequestsException"
"500":
description: "Internal Server Error"
content:
application/json:
schema:
$ref: "#/components/schemas/InternalServerErrorException"
x-amazon-apigateway-integration:
uri:
Fn::Sub: arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EventsApiLambda.Arn}:live/invocations
httpMethod: POST
type: aws_proxy
requestParameters:
integration.request.header.x-api-auth-user: "context.authorizer.x-api-auth-user"
integration.request.header.x-api-auth-resource-uri: "context.authorizer.x-api-auth-resource-uri"
integration.request.header.x-api-auth-type: "context.authorizer.x-api-auth-type"
integration.request.header.x-api-auth-resource-id: "context.authorizer.x-api-auth-resource-id"
integration.request.header.x-api-auth-resource-type: "context.authorizer.x-api-auth-resource-type"
integration.request.header.x-api-auth-resource-permissions: "context.authorizer.x-api-auth-resource-permissions"
passthroughBehavior: never
security:
- tokenAuthorizer: []
Lambda Authorizer Output (from API-Gateway-Execution-Logs-xxxx)
(023bd04b-e1c9-4980-ae14-xxxxx) Authorizer result body before parsing:
{
"principalId": "act-xxxxxxxxx",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:*:*:*"
}
]
},
"context": {
"x-api-auth-user": "act-xxxxxxx",
"x-api-auth-type": "email",
"x-api-auth-resource-id": "11eb2825-18cc-fb80-9d6c-xxxxx",
"x-api-auth-resource-type": "cb:event",
"x-api-auth-resource-permissions": "read,write"
}
}
I can see the output from authorizer reaching the api execution stage. But making it into neither the requestContext nor the multiValueHeaders.
API-Gateway-Execution-Logs_xxxx
(023bd04b-e1c9-4980-ae14-xxxx) Endpoint request headers: {X-Amz-Date=20210112T170314Z, x-amzn-apigateway-api-id=xxx, Accept=application/json, User-Agent=AmazonAPIGateway_xxxx, x-api-auth-type=email, Host=lambda.us-east-1.amazonaws.com, x-api-auth-resource-id=11eb2825-18cc-fb80-9d6c-xxxx, X-Amz-Content-Sha256=xxxxxx, X-Amzn-Trace-Id=Root=1-5ffdd64b-xxxxx;Parent=xxxx;Sampled=1, x-amzn-lambda-integration-tag=023bd04b-e1c9-4980-ae14-xxxxxx, Authorization=**********************282c30, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:38067 [TRUNCATED]
Lambda input
{
"path": "/events",
"isBase64Encoded": false,
"requestContext": {
"resourceId": "xxxxx",
"apiId": "xxxxx",
"resourcePath": "/events",
"httpMethod": "POST",
"requestId": "xxxxxx-15f9-4ca2-9a71-xxxxx",
"extendedRequestId": "xxxxxx=",
"accountId": "xxxx",
"identity": {
"userAgent": "PostmanRuntime/7.26.8",
"sourceIp": "xxxxx"
},
"authorizer": {
"principalId": "act-VNJQUexxxxx"
},
"stage": "v1",
"path": "/event/events",
"protocol": "HTTP/1.1",
"requestTime": "12/Jan/2021:17:53:06 +0000",
"requestTimeEpoch": xxx
},
....[TRUNCATED]....
}
Do I need to explicitly specify the authorizer result to be in each path's header/body?
Any idea?

Related

API Gateway -> SQS HTTP POST MessageAttributes

I have an API gateway setup which sends to SQS which fires a Lambda, I am trying to pass message attributes to the SQS but when I hit the endpoint in postman I keep getting a 400 Bad Request.. what is the right way to send the attributes over a JSON POST body
here is body from postman (have tried a few options based on this link)
"message": "Message",
"MessageAttributes": {
"Name": "Name",
"Type": "String",
"Value": "my value"
}
}
Here is how API Gateway is configured
Incase someone stumbles on this later here is worked from the CDK side
let intergation = new apiGateway.CfnIntegration(this, 'Integration', {
apiId: props.httpApi.httpApiId,
payloadFormatVersion: '1.0',
integrationType: 'AWS_PROXY',
credentialsArn: apigwRole.roleArn,
integrationSubtype: 'SQS-SendMessage',
requestParameters: {
QueueUrl: sqsqueue.queueUrl,
MessageBody: '$request.body',
MessageAttributes: '$request.body.MessageAttributes'
}
})
new apiGateway.CfnRoute(this, 'Route', {
apiId: props.httpApi.httpApiId,
routeKey: apiGateway.HttpRouteKey.with('/url/foo', apiGateway.HttpMethod.POST).key,
target: `integrations/${intergation .ref}`
}).addDependsOn(intergation);
and the cloudformation
MessageBody: $request.body
MessageAttributes: $request.body.MessageAttribute
then in post man the POST body content type as application/json
{
"message": "Message",
"MessageAttributes": {
"Attributes": {
"DataType": "String",
"StringValue": "my value"
}
}
}
the lamba would log out both separate for each Record from the event body
Records: [
{
....
body: 'Message',
attributes: [Object],
messageAttributes: [Object]
}
]
}
the messageAttributes object from above:
{
Attributes: {
stringValue: 'my value',
stringListValues: [],
binaryListValues: [],
dataType: 'String'
}
}
This is using AWS API Gateway v2 HTTP API also

How to allow API Gateway to return a 405 response

Hello AWS Cloud Gurus,
I am trying to allow my REST API to return a 405 when an unsupported HTTP verb is used on any resource.
I see there are ways to define GatewayResponses.
However, I don't see any obvious approach to return a 405 (other than to define it as the DEFAULT_4XX which seems incorrect)
ExampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
OpenApiVersion: '3.0.1'
GatewayResponses:
DEFAULT_4XX:
StatusCode: 405
ResponseTemplates:
"application/*": '{ "message": "Method Not Allowed" }'
Does anyone know how to do this?
One solution is to create a lambda function, attached to the API, to handle a specific endpoint which needs to indicate 405
ExampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
OpenApiVersion: '3.0.1'
MethodNotAllowedResponse:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs14.x
Handler: index.handler
InlineCode: |
let response;
exports.handler = (event, context, callback) => {
response = {
"statusCode": 405,
"headers": {
"Content-Type": "application/problem+json"
},
"body": JSON.stringify({
"type": "https://tools.ietf.org/html/rfc7231#section-6",
"status": 405,
"title": "Method Not Allowed",
"detail": `Method ${event.httpMethod} is not allowed on ${event.path}`
})
}
callback(null, response);
}
Events:
Televisions:
Type: Api
Properties:
Auth:
Authorizer: NONE
RestApiId: !Ref ExampleApi
Path: '/not/allowed/path'
Method: patch
This can be implemented as a mock integration that you then use for any methods that are not implemented/supported.
Integration request mapping
{
"statusCode": 405,
"message": "The invoked method is not supported on the API resource."
}

API Gateway CreateAuthorizer fails with InternalFailure

When I try to create custom authorizerer for my AWS API Gateway using CloudFormation, it freezes trying all the time to execute CreateAuthorizer call, but fails. Here is the minimum CloudFormation template with which I can reproduce that behavior:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
ApiGatewayV1:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "test"
ApiAuthorizerV1:
Type: "AWS::ApiGateway::Authorizer"
Properties:
RestApiId: !Ref "ApiGatewayV1"
Name: "test"
Type: "TOKEN"
AuthorizerUri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda::${AWS::AccountId}:function:${!stageVariables.AuthorizerLambdaName}/invocations"
IdentitySource: "method.request.header.Authorization"
AuthorizerResultTtlInSeconds: 3600
CloudTrail log:
"errorCode": "InternalFailure",
"errorMessage": "An unknown error occurred",
"requestParameters": {
"restApiId": "lweme6j3wk",
"createAuthorizerInput": {
"providerARNs": [],
"identitySource": "method.request.header.Authorization",
"authorizerResultTtlInSeconds": 3600,
"type": "TOKEN",
"name": "test",
"authorizerUri": "arn:aws:apigateway:eu-central-1:lambda:path/2015-03-31/functions/arn:aws:lambda::<ACCOUNT_ID>:function:${stageVariables.AuthorizerLambdaName}/invocations"
},
"template": false
},
"responseElements": null,
"requestID": "470e2efa-d3c1-11e7-b0cc-b7fd2383ef6b",
"eventID": "2ceccaa5-9b97-4b1e-93e5-3c4e6bca419d",
Ok, that was supper bizzare. When I explicitely specified region in the target lambda ARN it worked!
Replaced:
arn:aws:lambda::<ACCOUNT_ID>
With:
arn:aws:lambda:<REGION>:<ACCOUNT_ID>
(and yes, it works with ${AWS::Region}:${AWS::AccountId} placeholder, I used fixed values to check.

Describe AWS API Gateway Body Mapping Templates in CloudFormation

I looked though the documentation but didn't find a way to do this. I have a API Gateway method that has a Body Mapping Template, as in the picture attached.
How do I map this template in CloudFormation? (I'm using JSON). I added "PassthroughBehavior": "WHEN_NO_TEMPLATES", but haven't found a way to add the Content-Type mapping.
Thank you.
You can do something like this:
GETMethodRequest:
Type: "AWS::ApiGateway::Method"
DependsOn: ePlanningLambdaPermission
Properties:
RestApiId: !Ref YourAPI
AuthorizationType: NONE
HttpMethod: GET
RequestParameters:
method.request.querystring.name: true
MethodResponses:
- StatusCode: 200
ResourceId: !Ref ePlanningGISLocalitymapResource
Integration:
Type: AWS
IntegrationHttpMethod: POST
RequestTemplates:
"application/json": "{
\"body\" : $input.json('$'),
\"headers\": {
#foreach($header in $input.params().header.keySet())
\"$header\": \"$util.escapeJavaScript($input.params().header.get($header))\" #if($foreach.hasNext),#end
#end
},
\"method\": \"$context.httpMethod\",
\"params\": {
#foreach($param in $input.params().path.keySet())
\"$param\": \"$util.escapeJavaScript($input.params().path.get($param))\" #if($foreach.hasNext),#end
#end
},
\"query\": {
#foreach($queryParam in $input.params().querystring.keySet())
\"$queryParam\": \"$util.escapeJavaScript($input.params().querystring.get($queryParam))\" #if($foreach.hasNext),#end
#end
}
}"
IntegrationResponses:
- StatusCode: 302
You can do this as part of the RequestTemplates property described here.
It should look something like this:
"APIMethodGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RequestTemplates": {
"application/json": {
"Fn::Join": [
"",
[
"{\n \"StreamName\": \"my-kinesis\"\n",
"\n \"Data\": \"$util.base64encode($input.body)\"\n",
"\n \"PartitionKey\": \"1\"\n}"
]
]
}
},
"PassthroughBehavior": "WHEN_NO_TEMPLATES"
}
}

Set API Key to be required for AWS ApiGateway endpoint (Swagger import)

I try to define my AWS Api Gateway infrastructure using Swagger/OpenAPI. Everything is working so far, however I have problems enabling the need for an API-Key for my endpoints.
My Swagger file looks like this (shortened):
---
swagger: 2.0
basePath: /dev
info:
title: My API
description: Proof of concept
schemes:
- https
securityDefinitions:
api_key:
type: apiKey
name: X-Api-Key
in: header
paths:
/example-path:
options:
consumes:
- application/json
produces:
- application/json
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'GET,HEAD,OPTIONS'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
{}
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
get:
security:
- api_key: []
x-amazon-apigateway-integration:
# Further definition of the endpoint, calling Lambda etc...
Linked inside a CloudFormation template the Swagger file is processed successfully. But when I open the endpoint in the AWS Web Console, the flag for API Key Required is still false.
Any suggestions? Thanks.
Found the solution: the API key has to be named x-api-key (all lowercase).
It seems like only this way the setting is recognized during import.
To enable required API Key you need to add this "x-amazon-apigateway-api-key-source" : "HEADER" in security scheme block.
See an example:
"components" : {
"securitySchemes" : {
"api-key" : {
"type" : "apiKey",
"name" : "x-api-key",
"in" : "header",
"x-amazon-apigateway-api-key-source" : "HEADER"
}
}
}
It's an example using proxy requests.
Your JSON should be like this:
openapi3
{
"openapi": "3.0.3",
"info": {
"title": "User Portal",
"description": "API focused in User Portal.",
"version": "v1"
},
"paths": {
"users/{proxy+}": {
"options": {
"x-amazon-apigateway-integration": {
"httpMethod": "OPTIONS",
"payloadFormatVersion": "1.0",
"type": "MOCK"
}
},
"x-amazon-apigateway-any-method": {
"produces":[ "application/json"],
"parameters": [
{
"name": "proxy",
"in": "path",
"required": "true",
"type": "string"
}
],
"responses": {},
"security": [
{
"api-key": []
}
],
"x-amazon-apigateway-integration": {
"uri":"https://test.com.br/users/{proxy}",
"httpMethod":"ANY",
"type": "HTTP_PROXY"
}
}
}
},
"components" : {
"securitySchemes" : {
"api-key" : {
"type" : "apiKey",
"name" : "x-api-key",
"in" : "header",
"x-amazon-apigateway-api-key-source" : "HEADER"
}
}
}
}
In openapi2 you can add this in your yml.
swagger: 2.0
basePath: /dev
info:
title: My API
description: Proof of concept
schemes:
- https
securityDefinitions:
api_key:
type: apiKey
name: X-Api-Key
in: header
x-amazon-apigateway-api-key-source: HEADER
If you have troubles using api integration with openapi you can see this article: Working with API Gateway extensions to OpenAPI