aws api gateway header validation against matching pattern - amazon-web-services

I am creating an AWS API Gateway using terraform and openAPI specification with swagger. I need to add a request validator for validating the headers against a matching pattern [a-zA-z0-9]{10}. I was able to set up basic validator that checks if the header is empty or not but not able to validate with the pattern.
"x-amazon-apigateway-request-validators" : {
"full" : {
"validateRequestBody" : true,
"validateRequestParameters" : true,
"validateRequestHeaders" : true
},
"body-only" : {
"validateRequestBody" : true,
"validateRequestParameters" : false
}
},
"x-amazon-apigateway-request-validator" : "full",
"paths": {
"/validation": {
"get": {
"parameters": [
{
"in": "header",
"name": "x-request-id",
"required": true,
"type": "string",
"pattern" : "^[a-z0-9]{10}$"
},
{
"in": "query",
"name": "name",
"required": true,
"type": "string",
"pattern": "^[a-zA-Z]{5}$"
}
]
}
}
Please suggest if there is any way to achieve that

I can suggest a workaround for your requirement. You may use a "request based" Lambda authorizer and implement the validation logic within the Lambda function. If you need to validate just one header you may use 'token based' Lambda authorizer as well and specify a token validation regex.
Once the Lambda function determine whether the incoming headers are valid, it can grant access to the API.
You can check how to Configure a Lambda Authorizer Using the API Gateway Console.

instead of
"type": "string",
"pattern" : "^[a-z0-9]{10}$"
it should read
"schema": {
"type": "string",
"pattern" : "^[a-z0-9]{10}$"
}
see also here

Related

Callback url value in email for verifying account

This is an extension of Unable to validate account confirmation in WSO2 version 6.0 issue.
I have same regex pattern in my self-registration section. But when I'm creating users using rest API, the link which I got in the email is
https://localhost:9443/accountrecoveryendpoint/confirmregistration.do?confirmation=ce790759-1086-4870-a673-35b5927351d8&userstoredomain=PRIMARY&username=samyu&tenantdomain=carbon.super&callback={{callback}}
and when I created the user using manually the link which I got is
https://localhost:9443/accountrecoveryendpoint/confirmregistration.do?confirmation=dff024e7-d7e7-48ef-bb60-1c1c4d6f3b1c&userstoredomain=PRIMARY&username=sam&tenantdomain=carbon.super&callback=https%3A%2F%2Flocalhost%3A9443%2Fmyaccount.
So, the difference between these two links is that callback. So what configuration should I make in order to get the callback value
When you are trying this from the recovery portal, the callback value is set automatically. If you are trying with the REST API you need to include that in the request. The following is a sample JSON payload.
{
"user": {
"username": "kim",
"realm": "PRIMARY",
"password": "Password12!",
"claims": [
{
"uri": "http://wso2.org/claims/givenname",
"value": "kim"
},
{
"uri": "http://wso2.org/claims/emailaddress",
"value": "kimAndie#gmail.com"
},
{
"uri": "http://wso2.org/claims/lastname",
"value": "Anderson"
},
{
"uri": "http://wso2.org/claims/mobile",
"value": "+947729465558"
}
]
},
"properties": [
{
"key": "callback",
"value": "https://localhost:9443/myaccount"
}
]
}
Notice the way how you need to send the callback when using the REST API.

AWS API Gateway not picking up path and query parameter validation from OpenAPI/Swagger?

I imported an OpenAPI definition json into AWS API Gateway, and I noticed that none of path or query parameter validations works. I am hoping to do /card/{type}?userId={userId}, where type belongs to a set of enum values and userId with a regex pattern, as follows:
"paths: {
"/card/{type}": {
"get": {
"operationId": "..",
"parameters": [
{
"name": "type",
"in": "path",
"schema": {"$ref": "#/components/schemas/type}
},
{
"name": "userId"
"in": "query",
"schema": {
"type": "string",
"pattern": "<some regex>"
}
},
...
]
}
}
}
Turns out I can input whatever values I want for both path and query parameters. So I try exporting the OpenAPI file from AWS Console, and I got:
...
"parameters": [
{
"name": "type",
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "userId"
"in": "query",
"schema": {
"type": "string"
}
For API Gateway, are the validators not working for what's part of URL? because the requests with body seem to work fine. Or is there something I am missing?

How to reference existing API Gateway Authorizer in OpenApi endpoint definition

I've got an AWS SAM/CloudFormation template written in JSON which contains:
A definition of an API Gateway API, with endpoints defined using the
OpenAPI DefinitionBody format
A definition of an API Gateway Authorizer, defined using SAM/CF format
I'm trying to find a way to reference the Authorizer (2) for each of the endpoints (1) I wish to use the Authorizer for.
Here's the code:
Authorizer definition:
"LambdaAuthorizer":{
"Type": "AWS::ApiGateway::Authorizer",
"Properties":{
"IdentitySource":"method.request.header.Authorization",
"Type":"TOKEN",
"RestApiId":{
"Ref": "ApiName"
},
"AuthorizerUri": {
"Fn::Join" : ["", ["arn:aws:apigateway:", {"Ref": "AWS::Region"}, ":lambda:path/2015-03-31/functions/", {"Fn::GetAtt": ["AuthLambda", "Arn"]}, "/invocations"]]
},
"IdentityValidationExpression": "^[a-zA-Z0-9]{3,32}$",
"AuthorizerResultTtlInSeconds": 300,
"AuthorizerCredentials": {
"Fn::GetAtt": ["LambdaAuthorizerRole", "Arn"]
},
"Name":"lambda-authorizer"
}
},
Endpoint definition
"API": {
"Type": "AWS::Serverless::Api",
"Properties": {
...
"DefinitionBody": {
...
"paths": {
"/endpoint": {
"post": {
"responses": {
"200": {
"description": "200 response"
}
},
"x-amazon-apigateway-integration": {
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthLambda.Arn}/invocations"
},
"responses": {
"default": {
"statusCode": "200",
"contentHandling": "CONVERT_TO_TEXT"
}
},
"passthroughBehavior": "when_no_match",
"httpMethod": "POST",
"contentHandling": "CONVERT_TO_TEXT",
"type": "aws_proxy"
},
"security" : [{
"NAME OF OPEN API SECURITY DEFINITION":[] // Can I reference my existing Authorizer?
}]
}
},
}
}
}
}
As you can see I've found the OpenAPI security property, which according to the AWS documentation is used as a reference to a securityDefinitions where the Authorizer can be defined.
But I've already got my Authorizer defined in CloudFormation JSON. Can't I reference that instead?
API Gateway does not allow you to directly reference an existing Authorizer ID in the OpenAPI paths. However, there is a workaround. You can provide a securitySchemes definition. The format of securitySchemes will vary between OpenAPI 2.0 (aka Swagger) and OpenAPI 3.0 (documentation here).
Make sure that the details of the securitySchemes exactly match the existing API Authorizer. If everything matches, then API Gateway will use the existing Authorizer. If there are differences, then it will create a new Authorizer or overwrite the existing one. Ensure the following:
the security scheme object name is the same as the Authorizer name. In this case it is "lambda-authorizer"
type is "apiKey" for lambda authorizers
name is the HTTP header you're using for authorization (typically the "Authorization" header)
x-amazon-apigateway-authtype is "custom" for lambda authorizers
x-amazon-apigateway-authorizer has the correct type ("request" or "token"), the correct uri, ttl, and identity source
I've tested this with a regional REST API, so it may behave differently for other API Gateway types. The example below is a simplified version of the AWS PetStore example API.
{
"openapi" : "3.0.1",
"info" : {
"description" : "Your first API with Amazon API Gateway. This is a sample API that integrates via HTTP with our demo Pet Store endpoints",
"version" : "1",
"title" : "PetStore"
},
...
"paths" : {
"/pets" : {
"get" : {
...
"security" : [ {
"lambda-authorizer" : [ ]
} ],
"x-amazon-apigateway-integration" : {
...
}
}
}
},
"components" : {
"schemas" : {
...
},
"securitySchemes" : {
"lambda-authorizer" : {
"type" : "apiKey",
"name" : "Authorization",
"in" : "header",
"x-amazon-apigateway-authtype" : "custom",
"x-amazon-apigateway-authorizer" : {
"type" : "request",
"authorizerUri" : "arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${lambda_authorizer_arn}/invocations",
"authorizerResultTtlInSeconds" : 300,
"identitySource" : "method.request.header.Authorization"
}
}
}
}
}
In this example, you'd have to replace ${region} with your API Gateway region and ${lambdaauthorizer_arn} with the full ARN of your lambda function. The full string will look like this: arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:lambda-authorizer-function/invocations
TL;DR: If you want to use an existing Authorizer, you have to fully define it in the OpenAPI file.

How to test API Gateway methods with custom authorizer and empty $context.authorizer.* variables?

I have an API Gateway with a POST method that puts directly to a DynamoDB table. My method is also configured to use a custom authorizer via Lambda.
In my template mapping I'm consuming some of the authorizer variables, such as $context.authorizer.principalId or $context.authorizer.accountId. Simplified template mapping looks as follows:
{
"TableName": "$stageVariables.tableName",
"Item": {
"AccountId": {
"S": "$context.authorizer.accountId"
},
"Id": {
"S": "$context.requestId"
},
"Content": {
"S": "$input.path('$.content')"
},
"UserId": {
"S": "$context.authorizer.principalId"
}
}
}
Now, when I make an HTTP request to this API method deployed to a an actual stage, that request will go through the custom authorizer and provide / fill in all $context.authorizer.* variables in the template which might look like this:
{
"TableName": "MyTable",
"Item": {
"AccountId": {
"S": "12345"
},
"Id": {
"S": "6fd5ff08-34c0-11e7-bf96-591a565835b3"
},
"Content": {
"S": "my content"
},
"UserId": {
"S": "userid-123456789"
}
}
}
When testing the API method via the API Gateway Test button, the test request is bypassing the custom authorizer (which makes sense, since authorizer can be tested separately) and produces a result like this:
{
"TableName": "MyTable",
"Item": {
"AccountId": {
"S": ""
},
"Id": {
"S": "test-invoke-request"
},
"Content": {
"S": "my content"
},
"UserId": {
"S": ""
}
}
}
The following content is now invalid, since all fields get validated against the model, and getting the following validation error:
Endpoint response body before transformations: {"__type":"com.amazon.coral.validate#ValidationException","message":"One or more parameter values were invalid: An AttributeValue may not contain an empty string"}
Is there some way to specify authorizer variables when testing API Gateway methods?
Or is there some smart way to define a fallback variable in the template mapping so that when it resolves to empty, it will fall back to, e.g., test-invoke-principalid, just like $context.requestId gets that out of the box?
All I want is to be still able to use the API Gateway test feature while keeping all the validation / authorizer settings in place.
Sure we can look at adding placeholders for the test invoke feature. Certainly for the principalId. As for the custom context variables, that might be more difficult, we'll see. We eventually would like to have a better end-to-end testing solution as well.

ApiGateway CloudFormation without lambda

I am trying to create a template so that when i call api/divide/inputvalue, The api sends back response from DynamoDB which corresponds to inputvalue mapping.
Its pretty straight forward since i am fetching value directly from db without any business logic hence I don't need any lambda. But all the examples that I google or all tutorials they are using lambdas and i am now lost that how can i make it working without lambda
This is what I have so far. There is bug in this template right now since I haven't provided Uri in ApiGateway::Method. Which is what I am currently stuck at.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Deployment": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": { "Ref": "restApiName" },
"Description": "First Deployment",
"StageName": "StagingStage"
},
"DependsOn" : ["restApiMethod"]
},
"restApiMethod": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"AuthorizationType": "NONE",
"HttpMethod": "GET",
"ResourceId": {"Ref": "apiRestResource"},
"RestApiId": {"Ref": "restApiName"},
"Integration": {
"Type": "AWS",
"IntegrationHttpMethod": "GET",
"IntegrationResponses": [{"StatusCode": 200}],
"Uri": { "Fn::Sub":"arn.aws.apigateway:${AWS::Region}:dynamodb:action/${restApiName.Arn}"}
},
"MethodResponses": [{"StatusCode": 200}]
},
"DependsOn": ["apiRestResource"]
},
"apiRestResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {"Ref": "restApiName"},
"ParentId": {
"Fn::GetAtt": ["restApiName","RootResourceId"]
},
"PathPart": "divide"
},
"DependsOn": ["restApiName"]
},
"restApiName": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "CalculationApi"
}
}
}
}
According to the documentation, the Uri property is structured as follows for AWS service-proxy integration types:
If you specify AWS for the Type property, specify an AWS service that follows the form: arn:aws:apigateway:region:subdomain.service|service:path|action/service_api. For example, a Lambda function URI follows the 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.
The uri API Gateway property reference provides more details:
For AWS integrations, the URI should be of the form arn:aws:apigateway:{region}:{subdomain.service|service}:{path|action}/{service_api}. Region, subdomain and service are used to determine the right endpoint. For AWS services that use the Action= query string parameter, service_api should be a valid action for the desired service. For RESTful AWS service APIs, path is used to indicate that the remaining substring in the URI should be treated as the path to the resource, including the initial /.
For an AWS service proxy to the dynamodb service calling the Query Action, the Uri should be something like this (using the YAML short-form of Fn::Sub to insert a Ref for the current AWS region):
!Sub "arn:aws:apigateway:${AWS::Region}:dynamodb:action/Query"
As for your broader use-case of using API Gateway to access DynamoDB without using Lambda functions, refer to Andrew Baird's tutorial blog post, "Using Amazon API Gateway as a Proxy for DynamoDB", and translate the specified Management Console steps to corresponding CloudFormation template resources.