Verify APIGW is Caching Lambda Authorizer Response - amazon-web-services

I am currently running an API through APIGW, the endpoints are protected by a Lambda Authorizer. In order to improve performance, I enabled caching in APIGW for the Policy generated by the Authorizer.
But, I'm unsure how to effectively test if the cache is working properly. Is there some standard series of calls I could use, or some way to definitively check that the Policy is being cached?
Part of SAM Template:
EndpointConfiguration:
Type: PRIVATE
VPCEndpointIds: !Ref VPCEndpoints
Auth:
DefaultAuthorizer: DefAuthorizer
Authorizers:
DefAuthorizer:
FunctionPayloadType: TOKEN
FunctionInvokeRole: !Sub arn:aws:iam::${AWS::AccountId}:role/RoleName
FunctionArn: !ImportValue AuthorizerArn
Identity:
Headers:
- Authorization
ReauthorizeEvery: 3600

We can check two things:
Custom Authorizer Lambda will not be invoked at all when it is using the policy from cache.
You can enable Api Gateway Stage Logs to cloudwatch. you will see difference in logging.
When it is cached, you will see only one entry Using valid authorizer policy for principal
When it is not cached, you should see additional entires
Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda....
Authorizer result body before parsing: ... full policy that was returned by lambda

Related

How to construct AWS CloudFormation integration URI for AWS ApiGateway integration with S3, SQS, SNS, DynamoDB and other services?

Is there some place with samples on how to make a bunch of actions through ApiGateway integration? Looking how to upload object to S3, push item to SQS & SNS queues, make DynamoDB call and many other things, trying to find documentation on how to construct those paths.
I'm using CloudFormation template, which uses integration URI to setup this AWS ApiGateway integration with AWS services.
Can't find documentation talking how to make these URI paths for all kind of services.
When setting up the integration request with another AWS service action, the integration request URI is also an ARN.
For example, for the integration with the GetBucket action of Amazon S3, the integration request URI is an ARN of the following format:
arn:aws:apigateway:api-region:s3:path
See more: https://docs.aws.amazon.com/apigateway/latest/developerguide/integration-request-basic-setup.html
Dynamodb:
A bit more complicated then S3:
https://aws.amazon.com/blogs/compute/using-amazon-api-gateway-as-a-proxy-for-dynamodb/
SNS:
https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-proxy-integrate-service/
For SQS I have found cloudformation setup:
PostMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
ApiKeyRequired: "true"
HttpMethod: "POST"
ResourceId: !Ref "SomeResource"
RestApiId: !Ref "RestApi"
MethodResponses:
- StatusCode: 200
Integration:
Credentials: !GetAtt "RestApiRole.Arn"
IntegrationHttpMethod: "POST"
IntegrationResponses:
- StatusCode: 200
Type: "AWS"
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:sqs:action/SendMessage"
RequestParameters:
integration.request.querystring.QueueUrl: !Sub "'${SomeQueue}'"
integration.request.querystring.MessageBody: "method.request.body"
and here the code for RestApiRole:
RestApiRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Principal:
Service:
- "apigateway.amazonaws.com"
Effect: "Allow"
Policies:
- PolicyName: "InvokeLambda"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "lambda:InvokeFunction"
Resource: !GetAtt "LambdaFunction.Arn"
Effect: "Allow"
From Uri property documentation:
If you specify AWS for the Type property, specify an AWS service that follows this form: arn:aws:apigateway:region:subdomain.service|service:path|action/service_api. For example, a Lambda function URI follows this form: arn:aws:apigateway:region:lambda:path/path. The path is usually in the form /2015-03-31/functions/LambdaFunctionARN/invocations. For more information, see the uri property of the Integration resource in the Amazon API Gateway REST API Reference.
More descriptions and samples from another AWS documentation:
From these documentation samples & descriptions it seems there are 2 type of APIs - action based and path based.
Using Action based API šŸ˜
I think most, if not all support this. While those actions are available in IAM settings and all API documentations, while all AWS services are web services, aka they have API interfaces and those interfaces use Actions. Correct me if that's wrong for some service, but I think by following this structure should be possible to make any call to any service which has integration with API Gateway service.
Sometimes need to use path API šŸ˜­
Was trying to upload file to S3 with PutObject and it was giving error:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>MethodNotAllowed</Code>
<Message>The specified method is not allowed against this resource.</Message>
<Method>PUT</Method>
<ResourceType>SERVICE</ResourceType>
<RequestId>....</RequestId>
<HostId>....=</HostId>
</Error>
Replaced with path API format and it worked out. So the learning here is I will continue to try using action APIs first and if can't for that specific action - switch to path API while I feel Action API is more declarative.
Sample structure Action API:
arn:aws:apigateway:us-east-1:SERVICE_NAME:action/ACTION_NAME&Var1=Value1&Var2=Value2
Sample call to S3 service. Action name - GetObject. Documentation for this API Actions says there are 2 required properties - Bucket (bucket name) and Key. So full sample URI:
arn:aws:apigateway:us-east-1:s3:action/GetObject&Bucket=myDemoBucket1&Key=some/path/to/file
Same thing with path API:
arn:aws:apigateway:us-east-1:s3:path/myDemoBucket1/some/path/to/file
I found one way to get samples. Use console UI, make the endpoint, deploy to some stage and go to stage, select Export tab, and export as Swagger + API Gateway Extensions in Yaml format. While i use Yaml with cloudformation. Inside that Yaml there are all you need. If there are no "Stages", go to "Resources" and from dropdown select deploy and create Stage inside dialog.
Here are some different samples I was able to find for main services:
Invoke Lambda docs:
arn:aws:apigateway:api-region:lambda:path//2015-03-31/functions/arn:aws:lambda:lambda-region:account-id:function:lambda-function-name/invocations
The path part seems to map to API action from API docs:
DynamoDB blog post
You need to use HTTP method by API Action documentation + Api Action name + IntegrationRequest template to call DynamoDB.
Sample URI for Query action:
arn:aws:apigateway:us-east-1:dynamodb:action/Query
SNS blog post
Sample URI: arn:aws:apigateway:region:sns:action/Publish
With region: arn:aws:apigateway:us-east-1:sns:action/Publish
You need to pass in TopicArn and Message and other parameters through URL Query String Parameters. There is good thread on the topic: https://stackoverflow.com/a/64268791/1737158
https://docs.aws.amazon.com/sns/latest/api/API_Publish.html#API_Publish_Examples

AWS API Gateway integration with AWS Event Bridge(Cloudwatch Events) in Cloudformation Script

Original Requirement:
Create a route/path on AWS Api Gateway which connects API Gateway directly to AWS Event Bridge (Cloudwatch Events) and puts/pushes event on an event bus of it.
Was able to create it and executes just fine when done from AWS Console.
Actual Problem:
When writing the AWS Cloudformation script for this API Gateway, it looks like this:
EventsPostMethod:
Type: AWS::ApiGateway::Method
Properties:
ResourceId:
Ref: EventsResource
RestApiId:
Ref: RestAPI
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:cloudwatchEvents:action/PutEvents
RequestParameters:
integration.request.header.X-Amz-Target: "'AWSEvents.PutEvents'"
RequestTemplate:
some-script-here...
Notice the Uri value:
"arn:aws:apigateway:${AWS::Region}:cloudwatchEvents:action/PutEvents"
arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}
According to AWS Docs the value of uri should be following:
For AWS or AWS_PROXY integrations, the URI is of the form arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}. Here, {Region} is the API Gateway region (e.g., us-east-1); {service} is the name of the integrated AWS service (e.g., s3); and {subdomain} is a designated subdomain supported by certain AWS service for fast host-name lookup. action can be used for an AWS service action-based API, using an Action={name}&{p1}={v1}&p2={v2}... query string. The ensuing {service_api} refers to a supported action {name} plus any required input parameters. Alternatively, path can be used for an AWS service path-based API. The ensuing service_api refers to the path to an AWS service resource, including the region of the integrated AWS service, if applicable. For example, for integration with the S3 API of GetObject, the uri can be either arn:aws:apigateway:us-west-2:s3:action/GetObject&Bucket={bucket}&Key={key} or arn:aws:apigateway:us-west-2:s3:path/{bucket}/{key}
You must have noticed that I replaced the service with cloudwatchEvents in the above mentioned uri.
Now, error Given by AWS Cloudformation Console during Publish of API Gateway:
AWS Service of type cloudwatchEvents not supported (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 07bae22c-d198-4595-8de9-6ea23763eff5; Proxy: null)
Now I have tried replacing service with
cloudwatch
eventBridge
cloudwatchEvent
event-bus
This is the real problem. What should I place in service in uri so that it accepts ?
Based on the comments,
The URI should be something like below for events:
arn:aws:apigateway:${AWS::Region}:events:action/PutEvents

AWS API Gateway with Cognito Authorization using multiple user pools

I have multiple Cognito user pools which I use to separate users for different applications. I also have a set of APIs which are defined in API Gateway. These APIs are common and multiple applications can use them. I am trying to control which applications have access to which APIs using a Cognito resource server and custom scopes.
This is the guide I've been following: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
The problem I'm having is that I have to specify a user pool when creating an API Gateway authorizer. I can create multiple authorizers but I only seem to be able to select one when attaching an authorizer to an API Gateway method.
This set up means than only one user pool can ever have access to an API in API Gateway when using the Cognito authorizer. Is this correct or have I missed something?
My only option seems to be using a Lambda authorizer and doing all this manually. However, this means that I have to store a mapping between API endpoints/methods and custom scopes. If I were to take this approach, how would I verify that an access token has access to the endpoint in the incoming request?
I've done something similar but not exactly what you are doing, but maybe it'll lead you in the right direction.
So I think you're right in that you'll need to do a Lambda authorizer, but then assume the role of the cognito user. Then if that user doesn't have access to do something IAM will blow it up.
client = boto3.client('sts')
role=event['requestContext']['authorizer']['claims']['cognito:preferred_role']
assumed_role_object = client.assume_role(
RoleArn=role,
RoleSessionName='APIrole'
)
credentials=assumed_role_object['Credentials']
dynamo_resource=boto3.resource(
'dynamodb',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
There is a solution posted here:
How to use multiple Cognito user pools for a single endpoint with AWS API Gateway?
I found Abhay Nayak answer useful, it helped me to achieve my scenario:
Allowing authorization for a single endpoint, using JWTs provided by different Cognitos, from different aws accounts. Using cognito user pool authorizer, not custom lambda authorizer.
Here is the authorizer and endpoint from my serverless .yml template:
functions:
service:
handler: service.service
events:
- http:
path: service
method: get
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
resources:
Resources:
ApiGatewayAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 300
Name: API_AUTH_cognito_authorizer
IdentitySource: method.request.header.Authorization
RestApiId:
Ref: ApiGatewayRestApi
Type: COGNITO_USER_POOLS
ProviderARNs:
- arn:aws:cognito-idp:us-east-1:account1:userpool/userpool1
- arn:aws:cognito-idp:us-east-1:account1:userpool/userpool2
- arn:aws:cognito-idp:us-east-1:account2:userpool/userpool3
- arn:aws:cognito-idp:us-east-1:account2:userpool/userpool4

In AWS API Gateway, can I use a Usage Plan without attaching an API Key?

AWS documentation on API Gateway Usage Plans all imply that they're created with/attached to API Keys, but don't state how it will behave without one. I have an unauthorized API that I would like to apply throttling to. Can I create a Usage Plan, attach it to a resource, without associating an API Key to it? Will it work?
Context: I'm trying to use CloudFormation's Java SDK to define a stack, and I'm unable to figure out how to specify resource-specific throttles programmatically. I will also accept an answer that tells me how to do this without having to use the console.
Can I create a Usage Plan, attach it to a resource, without
associating an API Key to it? Will it work?
No, but based on your use case I think you want server-side throttling rather than per-client throttling. The docs outline the distinction:
Amazon API Gateway provides two basic types of throttling-related
settings:
Server-side throttling limits are applied across all clients. These limit settings exist to prevent your APIā€”and your accountā€”from being
overwhelmed by too many requests.
Per-client throttling limits are applied to clients that use API keys associated with your usage policy as client identifier.
Set server-side method throttling in AWS console
You can set default rate and burst limits for all methods per stage. In the AWS console, this can be done by going to Stages > your_stage > Settings > Default Method Throttling.
Set server-side method throttling in a Cloudformation template
I'm unable to figure out how to specify resource-specific throttles
programmatically.
See the below Cloudformation template snippet for creating a stage with method settings, from here:
Resources:
Prod:
Type: AWS::ApiGateway::Stage
Properties:
StageName: Prod
Description: Prod Stage
RestApiId: !Ref MyRestApi
DeploymentId: !Ref TestDeployment
DocumentationVersion: !Ref MyDocumentationVersion
ClientCertificateId: !Ref ClientCertificate
Variables:
Stack: Prod
MethodSettings:
- ResourcePath: /
HttpMethod: GET
MetricsEnabled: 'true'
DataTraceEnabled: 'false'
- ResourcePath: /stack
HttpMethod: POST
MetricsEnabled: 'true'
DataTraceEnabled: 'false'
ThrottlingBurstLimit: '999'
- ResourcePath: /stack
HttpMethod: GET
MetricsEnabled: 'true'
DataTraceEnabled: 'false'
ThrottlingBurstLimit: '555'
Unfortunately, usage plans do not work without an api key. From official documentation.
A usage plan specifies who can access one or more deployed API stages and methodsā€”and also how much and how fast they can access them. The plan uses API keys to identify API clients and meters access to the associated API stages for each key. It also lets you configure throttling limits and quota limits that are enforced on individual client API keys.
You can configure your ThrottleSettings in AWS::ApiGateway::UsagePlan like so:
Resources:
UsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ThrottleSettings:
BurstLimit:
RateLimit:
From API Gateway's documentation on Throttling API Requests for Better Throughput:
Amazon API Gateway provides two basic types of throttling-related
settings:
Server-side throttling limits are applied across all clients. These limit settings exist to prevent your APIā€” and your account ā€” from
being overwhelmed by too many requests.
Per-client throttling limits are applied to clients that use API keys associated with your usage policy as client identifier.

How to configure CORS for an AWS API Gateway Custom Authorizer?

I have an API powered by API Gateway and Lambda that uses a custom authorizer.
For successful requests, it passes through the authorizer and then my Lambda can return proper responses with CORS headers with no problems.
However, for unsuccessful authorizations (eg. invalid tokens), I get no CORS headers and that causes my client app (which uses fetch API) to throw.
How do I setup CORS for an API that uses a custom authorizer?
Based from this answer and this AWS documentation page, I was able to figure out how to solve it.
The solution is to add the following in my serverless.yml:
resources:
Resources:
AuthorizerApiGatewayResponse:
Type: "AWS::ApiGateway::GatewayResponse"
Properties:
ResponseParameters:
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
ResponseType: UNAUTHORIZED
RestApiId: {"Ref" : "ApiGatewayRestApi"}
StatusCode: "401"