Search code examples
swaggeraws-cloudformationaws-api-gatewayserverless-application-model

AWS API Gateway api key required not set to 'true' after deployment


I have a .NET solution that uses a SAM template to generate cloudformation to deploy the stack. I am expecting the deployment - once complete - to have API Key Required = true on at least one of the methods. However after deployment, the keys and usage plans are created, but in the console the api key required is still set to false?

See below:

enter image description here

My SAM template:

    "ServerlessRestApi": {
        "Type": "AWS::ApiGateway::RestApi",
        "Properties": {
            "Description":"This is a placeholder for the description of this web api",
            "Body": {
                "info": {
                    "version": "1.0",
                    "title": {
                        "Ref": "AWS::StackName"
                    }
                },
                "x-amazon-apigateway-api-key-source": "HEADER",
                "paths": {
                    "datagw/general/table/get/{tableid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        },
                        "security":[
                                    {
                                        "api_key":[]
                                    }
                                ]},
                                "securityDefinitions":{
                                    "api_key":{
                                        "type":"apiKey",
                                        "name":"x-api-key",
                                        "in":"header"
                                }
                    },
                    "/": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/tables/{tableid}/{columnid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    }
                },
                "swagger": "2.0"
            }
        }
    },

I am not that familiar with swagger definitions, I know only the basics of SAM and CloudFormation. What am I missing here? I have reviewed other answers on stack overflow and believe I've copied the configuration correctly.

When I check the generated CloudFormation, my entries regarding x-api-key are not even present in the template?

  "ServerlessRestApi": {
        "Type": "AWS::ApiGateway::RestApi",
        "Properties": {
            "Body": {
                "info": {
                    "version": "1.0",
                    "title": {
                        "Ref": "AWS::StackName"
                    }
                },
                "paths": {
                    "datagw/general/table/get/{tableid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/datagw/general/webhook/ccnotify": {
                        "post": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectNotification.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/tables/{tableid}/{columnid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/datagw/general/post/sohupdate": {
                        "post": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectStockUpdate.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    }
                },
                "swagger": "2.0"
            }
        }
    },

EDIT: This is what I have worked up to, but still API key required is not set to true in the API once the deployment has completed.

"ServerlessRestApi": {
    "Type": "AWS::ApiGateway::RestApi",
    "Properties": {
        "Description":"InSite Web API Version 2.0.0.0",
        "Body": {
            "swagger": "2.0",
            "info": {
                "version": "1.0",
                "title": {
                    "Ref": "AWS::StackName"
                }
            },
            "x-amazon-apigateway-api-key-source" : "HEADER",
            "schemes":["https"],
            "paths": {
                "tables/query/{tableid}": {
                    "get": {
                        "x-amazon-apigateway-integration": {
                            "httpMethod": "GET",
                            "type": "aws_proxy",
                            "uri": {
                                "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                            }
                        },
                        "responses": {},
                            "security": [
                            {
                                "api_key": []
                            }
                        ]
                    }
                },
                "/products/update/": {
                    "post": {
                        "x-amazon-apigateway-integration": {
                            "httpMethod": "POST",
                            "type": "aws_proxy",
                            "uri": {
                                "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectStockUpdate.Arn}/invocations"
                            }
                        },
                        "responses": {}
                    }
                },
                "/": {
                    "get": {
                        "x-amazon-apigateway-integration": {
                            "httpMethod": "GET",
                            "type": "aws_proxy",
                            "uri": {
                                "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                            }
                        },
                        "responses": {}
                    }
                },
                "/tables/{tableid}/{columnid}": {
                    "get": {
                        "x-amazon-apigateway-integration": {
                            "httpMethod": "GET",
                            "type": "aws_proxy",
                            "uri": {
                                "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                            }
                        },
                        "responses": {}
                    }
                }
            },
            "securityDefinitions": {
                "api_key": {
                    "type": "apiKey",
                    "name": "x-api-key",
                    "in": "header"
                }
            }
        }
    }
},

Solution

  • So first off, if you are using the SAM framework, then why not try the serverless API (AWS::Serverless::Api) which has an Auth object where you can turn on ApiKeyRequired.

    https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapi

    "ServerlessRestApi": {
        "Type": "AWS::Serverless::Api",
        "Properties": {
            "Description":"InSite Web API Version 2.0.0.0",
            "Auth": {
                "ApiKeyRequired": "true"
            },
            "DefinitionBody": {
                "swagger": "2.0",
                "info": {
                    "version": "1.0",
                    "title": {
                        "Ref": "AWS::StackName"
                    }
                },
                "x-amazon-apigateway-api-key-source" : "HEADER",
                "schemes":["https"],
                "paths": {
                    "tables/query/{tableid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "GET",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                                }
                            },
                            "responses": {},
                                "security": [
                                {
                                    "api_key": []
                                }
                            ]
                        }
                    },
                    "/products/update/": {
                        "post": {
                         "x-amazon-apigateway-integration": {
                                "httpMethod": "POST",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectStockUpdate.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "GET",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    },
                    "/tables/{tableid}/{columnid}": {
                        "get": {
                            "x-amazon-apigateway-integration": {
                                "httpMethod": "GET",
                                "type": "aws_proxy",
                                "uri": {
                                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                                }
                            },
                            "responses": {}
                        }
                    }
                },
                "securityDefinitions": {
                    "api_key": {
                        "type": "apiKey",
                        "name": "x-api-key",
                        "in": "header"
                    }
                }
            }
        }
    },
    

    If for some reason you cannot use the serverless, you might be trying to overload the RestApi (which is fine, but you lose some of the other fine grain options). For full disclosure I do not work with API gateway in this way (I use the serverless transform) so this is all from reading the documentation and not from experiance.

    I would try creating a bare bones AWS::ApiGateway::RestApi and then attach an AWS::ApiGateway::Method to the RestApi by reference it though RestApiId.

    [1] https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-method.html