serverless-offline: Custom authorizer will not work if http_proxy is specified - aws-api-gateway

I want to use serverless-offline to authenticate with a custom authorizer.
The file serverless.yml is shown below.
functions:
auth:
handler: src/authorizer.handler
...
custom:
serverless-offline:
httpPort: 4000
resourceRoutes: true
...
resources:
Resources:
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: protected
RestApiId:
Ref: ApiGatewayRestApi
ProxyPathResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Ref: ProxyResource
PathPart: "{proxy+}"
RestApiId:
Ref: ApiGatewayRestApi
ProxyPathMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: "CUSTOM"
AuthorizerId:
Ref: AuthApiGatewayAuthorizer
ResourceId:
Ref: ProxyPathResource
RestApiId:
Ref: ApiGatewayRestApi
HttpMethod: ANY
RequestParameters:
method.request.path.proxy: false
MethodResponses:
- StatusCode: 200
- StatusCode: 401
Integration:
IntegrationHttpMethod: ANY
Type: HTTP_PROXY
Uri: https://my-json-server.typicode.com/{proxy}
RequestParameters:
integration.request.path.proxy: method.request.path.proxy
IntegrationResponses:
- StatusCode: 200
- StatusCode: 401
Run it.
$ sls offline
...
offline: ANY /development/auth-test/{proxy*} -> https://my-json-server.typicode.com/{proxy}
offline: [HTTP] server ready: http://localhost:4000 🚀
After that, accessing the following uri will return a normal response.
The custom authorizer does not seem to be working.
$ curl 'http://localhost:4000/development/protected/typicode/demo/profile'
{"name": "typicode"}
Running curl through the api-gateway will result in 401
$ sls deploy
$ curl 'https://xxxx.execute-api.{region}.amazonaws.com/development/protected/typicode/demo/profile' // This is a 401
$ curl 'https://xxxx.execute-api.{region}.amazonaws.com/development/protected/typicode/demo/profile' --header 'Authorization: Bearer {valid_token}' // This is 200
Does serverless-offline go through authentication with http_proxy?
Pardon my broken English!

Related

How to get API Gateway to validate the pattern of HTTP query string parameters?

I'm trying to get API Gateway to do the above. See template at the bottom - there is a single message parameter, which I've tried to restrict to either foo or bar values, via specification of an AWS::ApiGateway::Model resource, bound to Content-Type application/x-www-form-urlencoded.
Now APIGW will validate the presence of the message parameter -
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?messag=foo"
{"message": "Missing required request parameters: [message]"}
But it doesn't seem to validate or restrict the value sent -
curl -H "Content-Type: application/x-www-form-urlencoded" "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/1-0-0/hello?message=whatever"
you sent 'whatever'
Any idea what I am doing wrong here?
AWSTemplateFormatVersion: '2010-09-09'
Outputs:
PublicApiEndpoint:
Value:
Fn::Sub: https://${PublicApiRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${PublicApiStage}
Parameters:
MemorySizeDefault:
Default: '512'
Type: String
RuntimeVersion:
Default: '3.8'
Type: String
TimeoutDefault:
Default: '5'
Type: String
Resources:
HelloFunction:
Properties:
Code:
ZipFile: |
def handler(event, context):
message=event["queryStringParameters"]["message"]
response="you sent '%s'" % message
return {'statusCode': 200,
'headers': {"Content-Type": "text/plain"},
'body': response}
Handler: index.handler
MemorySize:
Ref: MemorySizeDefault
Role:
Fn::GetAtt:
- HelloFunctionRole
- Arn
Runtime:
Fn::Sub: python${RuntimeVersion}
Timeout:
Ref: TimeoutDefault
Type: AWS::Lambda::Function
HelloFunctionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Version: '2012-10-17'
Policies:
- PolicyDocument:
Statement:
- Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Effect: Allow
Resource: '*'
Version: '2012-10-17'
PolicyName:
Fn::Sub: hello-function-role-policy-${AWS::StackName}
Type: AWS::IAM::Role
HelloEndpointMethod:
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri:
Fn::Sub:
- arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${arn}/invocations
- arn:
Fn::GetAtt:
- HelloFunction
- Arn
RequestParameters:
"method.request.querystring.message": true
RequestValidatorId:
Ref: HelloEndpointValidator
RequestModels:
"application/x-www-form-urlencoded": HelloEndpointModel
ResourceId:
Ref: HelloEndpointResource
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Method
HelloEndpointPermission:
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: HelloFunction
Principal: apigateway.amazonaws.com
SourceArn:
Fn::Sub: arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PublicApiRestApi}/${PublicApiStage}/GET/hello
Type: AWS::Lambda::Permission
HelloEndpointResource:
Properties:
ParentId:
Fn::GetAtt:
- PublicApiRestApi
- RootResourceId
PathPart: hello
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Resource
HelloEndpointValidator:
Properties:
RestApiId:
Ref: PublicApiRestApi
ValidateRequestParameters: true
Type: AWS::ApiGateway::RequestValidator
HelloEndpointModel:
Properties:
RestApiId:
Ref: PublicApiRestApi
ContentType: 'application/x-www-form-urlencoded'
Name: HelloEndpointModel
Schema:
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
properties:
message:
type: string
pattern: "^((foo)|(bar))$"
required:
- message
Type: AWS::ApiGateway::Model
PublicApiDeployment:
DependsOn:
- HelloEndpointMethod
Properties:
RestApiId:
Ref: PublicApiRestApi
Type: AWS::ApiGateway::Deployment
PublicApiRestApi:
Properties:
Name:
Fn::Sub: public-api-rest-api-${AWS::StackName}
Type: AWS::ApiGateway::RestApi
PublicApiStage:
Properties:
DeploymentId:
Ref: PublicApiDeployment
RestApiId:
Ref: PublicApiRestApi
StageName: 1-0-0
Type: AWS::ApiGateway::Stage
Apparently this is not possible - https://twitter.com/alexbdebrie/status/1603059764489252864?s=20&t=LI7OUEw9b5qxCIvkeP4AYQ

I can't add proxy with ApiGateway using Serverless Framework v3

i’m trying to add proxy to my lambda function, but i’m getting error:
“CREATE_FAILED: ApiGatewayResourceJokes (AWS::ApiGateway::Resource)
Resource handler returned message: “Another resource with the same
parent already has this name: example (Service: ApiGateway, Status
Code: 409, Request ID: 3fb2a85c-6fd2-4414-ad3f-72ee095da48b)”
(RequestToken: f33a6da3-be94-9696-3fd6-35a7bc52ef0a, HandlerErrorCode:
AlreadyExists)”
example:
handler: lambdas/endpoints/proxy.handler
events:
- http: GET /example
ProxyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi # our default Rest API logical ID
- RootResourceId
PathPart: jokes # the endpoint in your API that is set as proxy
RestApiId:
Ref: ApiGatewayRestApi
ProxyMethod:
Type: AWS::ApiGateway::Method
Properties:
ResourceId:
Ref: ProxyResource
RestApiId:
Ref: ApiGatewayRestApi
HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ?
MethodResponses:
- StatusCode: 200
Integration:
IntegrationHttpMethod: POST
Type: HTTP
Uri: http://api.icndb.com/jokes/random # the URL you want to set a proxy to
IntegrationResponses:
- StatusCode: 200

Cloudformation - Type: "AWS::ApiGateway::Method" returns {"message": "Internal server error"}

I get {"message": "Internal server error"} return from AWS API gateway when I write a Cloudformation template. Everything works well if I configure them on AWS console.
curl https://abcdefgh.execute-api.ap-southeast-1.amazonaws.com/demo/getitems
{"message": "Internal server error"}
I check all parts in the CF template and I think the problem is in "AWS::ApiGateway::Method". All configuration/parameters are the same between CF template and AWS console ( I checked them on console). When I delete the method created by CF, re-create and re-deploy it manually, it works.
I believe that "AWS::Lambda::Permission" works because I see it in Resource-based policy.
Does anyone have a solution to this problem? Thank you in advance.
The CF template:
apigwGETMethod:
DependsOn:
- apiResourceGET
- lambdaFunction3
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
ResourceId: !Ref apiResourceGET
RestApiId: !Ref apiGateway
Integration:
IntegrationHttpMethod: "GET"
Type: "AWS"
Credentials: !GetAtt ServerlessIAMRole.Arn
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt lambdaFunction3.Arn
IntegrationResponses:
- ResponseTemplates:
"application/json": ""
StatusCode: 200
MethodResponses:
- ResponseModels:
"application/json": "Empty"
StatusCode: 200
apiGatewayDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- apiGatewayGETMethod
Properties:
RestApiId: !Ref apiGateway
StageName: !Ref apiGatewayStageName
lambdaFunction3:
DependsOn:
- ServerlessIAMRole
Type: "AWS::Serverless::Function"
Properties:
FunctionName: !Ref lambdaFunction3Name
Handler: lambda_function3.lambda_handler
Description: "get all items from DynamoDB table"
Runtime: python3.6
CodeUri: .
Role: !GetAtt ServerlessIAMRole.Arn
lambda3ApiGatewayInvoke:
DependsOn:
- lambdaFunction3
- apiGatewayGETMethod
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt lambdaFunction3.Arn
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*"

Create methods under / using CF for API Gateway

How do I create methods under API Gateway's root / folder using CF? So for example I have a Gateway that looks like the following:
/
OPTIONS
POST
However when trying to do that with CF I get:
Resource's path part only allow a-zA-Z0-9._- and curly braces at the beginning and the end. So my PathPart is the offending line.
ApiGate:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt
- ApiGateApi
- RootResourceId
PathPart: '{/}'
RestApiId: !Ref ApiGateApi
I can change the PathPart to something else but then it creates it as a child object under / which is what I don't want.
Turns out after adding the following to my AWS::ApiGateway::Method it works now.
MyMethodOPTIONS:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !GetAtt MyRestApi.RootResourceId
Here is more context into my Template:
ApiGatewayMethodOPTIONS:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !GetAtt ApiGatewayRestApi.RootResourceId
RestApiId: !Ref ApiGatewayRestApi
AuthorizationType: NONE
HttpMethod: OPTIONS
Integration:
Type: MOCK
IntegrationResponses:
- ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/json: ''
StatusCode: '200'
PassthroughBehavior: NEVER
RequestTemplates:
application/json: '{"statusCode": 200}'
MethodResponses:
- ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
StatusCode: '200'
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
ApiKeySourceType: HEADER
EndpointConfiguration:
Types:
- REGIONAL
Name: SearchAPI
This fixes my problem, in my case I needed to have 2 methods:
1. will respond to requests to root e.g. https://<api-url>/prod or https://<api-url>/prod/. This will use the RootResourceId of the API Gateway:
ResourceId: !GetAtt myApiGateway.RootResourceId
this will respond to requests to whatever has been set under https://<api-url>/prod/. Could be petstore or if using {proxy+} then the backend workload will try to resolve the request. It will refer to the Resource type defined in the template:
ResourceId: !Ref myResource

How to enable ApiKeyRequired for an Api Method?

I want to use the standard API Keys feature of API Gateway. If I use standard cloudformation this is possible by setting the property ApiKeyRequired to true for a method. How can I do this with SAM?
I tried using swagger but that does not seem to work:
swagger: "2.0"
info:
title: !Ref AWS::StackName
paths:
"/machines/{resourceid}":
get:
parameters:
- name: resourceid
in: path
type: string
required: true
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambda.Arn}/invocations
responses: {}
security:
- authorizer: []
securityDefinitions:
authorizer:
type: apiKey
name: Authorization
in: header
Any suggestions?
The following swagger definition works:
DefinitionBody:
swagger: "2.0"
info:
title: !Ref AWS::StackName
x-amazon-apigateway-api-key-source : "HEADER"
paths:
"/machines/{resourceId}":
get:
parameters:
- name: resourceId
in: path
type: string
required: true
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MessagingServiceTestHandler.Arn}/invocations
responses: {}
security:
- api_key: []
securityDefinitions:
api_key:
type: "apiKey"
name: "x-api-key"
in: "header"
The name of the api key header must be x-api-key rather than the standard Authorization header.