Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automating CORS using BOTO3 for AWS Api Gateway

I have been attempting for the last many hours to configure CORS for the AWS Api Gateway. I have attempted to copy verbatim what the "Enable CORS" button does within the aws console. But even though every method looks identical in the console, POSTing to the rest API works with the "Enable CORS" button but returns a 500 permission error when CORS is set up using my code.

This is the code that is relevant to CORS setup:

# Set the put method response of the POST method
self.apigateway.put_method_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='POST',
    statusCode='200',
    responseParameters={
        'method.response.header.Access-Control-Allow-Origin': False
    },
    responseModels={
        'application/json': 'Empty'
    }
)

# Set the put integration response of the POST method
self.apigateway.put_integration_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='POST',
    statusCode='200',
    responseParameters={
        'method.response.header.Access-Control-Allow-Origin': '\'*\''
    },
    responseTemplates={
        'application/json': ''
    }
)

# Add an options method to the rest api
api_method = self.apigateway.put_method(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    authorizationType='NONE'
)

# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    type='MOCK',
    requestTemplates={
        'application/json': ''
    }
)

# Set the put method response of the OPTIONS method
self.apigateway.put_method_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    statusCode='200',
    responseParameters={
        'method.response.header.Access-Control-Allow-Headers': False,
        'method.response.header.Access-Control-Allow-Origin': False,
        'method.response.header.Access-Control-Allow-Methods': False
    },
    responseModels={
        'application/json': 'Empty'
    }
)

# Set the put integration response of the OPTIONS method
self.apigateway.put_integration_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    statusCode='200',
    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': ''
    }
)

This is the response from get-method for POST and OPTIONS when CORS is enabled through the AWS console:

{
    "httpMethod": "POST",
    "apiKeyRequired": false,
    "methodIntegration": {
        "httpMethod": "POST",
        "cacheKeyParameters": [],
        "integrationResponses": {
            "200": {
                "responseParameters": {
                    "method.response.header.Access-Control-Allow-Origin": "'*'"
                },
                "statusCode": "200",
                "responseTemplates": {
                    "application/json": null
                }
            }
        },
        "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
        "requestTemplates": {
            "application/json": null
        },
        "cacheNamespace": "o9h9b8tzo2",
        "type": "AWS"
    },
    "methodResponses": {
        "200": {
            "responseParameters": {
                "method.response.header.Access-Control-Allow-Origin": false
            },
            "statusCode": "200",
            "responseModels": {
                "application/json": "Empty"
            }
        }
    },
    "authorizationType": "NONE"
}
{
    "requestParameters": {},
    "httpMethod": "OPTIONS",
    "methodResponses": {
        "200": {
            "statusCode": "200",
            "responseParameters": {
                "method.response.header.Access-Control-Allow-Headers": false,
                "method.response.header.Access-Control-Allow-Methods": false,
                "method.response.header.Access-Control-Allow-Origin": false
            },
            "responseModels": {
                "application/json": "Empty"
            }
        }
    },
    "apiKeyRequired": false,
    "methodIntegration": {
        "cacheNamespace": "o9h9b8tzo2",
        "type": "MOCK",
        "requestTemplates": {
            "application/json": "{\"statusCode\": 200}"
        },
        "integrationResponses": {
            "200": {
                "responseTemplates": {
                    "application/json": null
                },
                "statusCode": "200",
                "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": "'*'"
                }
            }
        },
        "cacheKeyParameters": []
    },
    "authorizationType": "NONE"
}

And this is the response of get-method from CORS being enabled using my code:

{
    "authorizationType": "NONE",
    "httpMethod": "POST",
    "methodIntegration": {
        "requestTemplates": {
            "application/json": null
        },
        "cacheNamespace": "308o168qal",
        "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
        "httpMethod": "POST",
        "cacheKeyParameters": [],
        "integrationResponses": {
            "200": {
                "responseParameters": {
                    "method.response.header.Access-Control-Allow-Origin": "'*'"
                },
                "statusCode": "200",
                "responseTemplates": {
                    "application/json": null
                }
            }
        },
        "type": "AWS"
    },
    "apiKeyRequired": false,
    "methodResponses": {
        "200": {
            "responseParameters": {
                "method.response.header.Access-Control-Allow-Origin": false
            },
            "responseModels": {
                "application/json": "Empty"
            },
            "statusCode": "200"
        }
    }
}
{
    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "methodIntegration": {
        "integrationResponses": {
            "200": {
                "statusCode": "200",
                "responseTemplates": {
                    "application/json": null
                },
                "responseParameters": {
                    "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
                    "method.response.header.Access-Control-Allow-Origin": "'*'",
                    "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
                }
            }
        },
        "cacheNamespace": "bm4zmvzkdk",
        "type": "MOCK",
        "cacheKeyParameters": [],
        "requestTemplates": {
            "application/json": "{\"statusCode\": 200}"
        }
    },
    "requestParameters": {},
    "methodResponses": {
        "200": {
            "statusCode": "200",
            "responseModels": {
                "application/json": "Empty"
            },
            "responseParameters": {
                "method.response.header.Access-Control-Allow-Methods": false,
                "method.response.header.Access-Control-Allow-Origin": false,
                "method.response.header.Access-Control-Allow-Headers": false
            }
        }
    },
    "httpMethod": "OPTIONS"
}

I can not see a single difference, what am I doing wrong?

As per the request of MikeD at AWS the POST request is made from javascript within a file sitting within s3:

function post_request() {
    var xhr = new XMLHttpRequest();
    var params = JSON.stringify({
        request: "registerUser",
        user:{ 
            username: document.getElementById("usernameInput").value, 
            email: document.getElementById("emailInput").value,
            password: document.getElementById("passwordInput").value
        }
    });
    xhr.open("POST", "$(endpoint_url)", true);
    xhr.setRequestHeader("Content-type", "application/json");
    xhr.setRequestHeader("x-api-key", "$(api_key)");
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status === 200){
                alert("You are registered!");
            }
            else{
                alert("Could not register. Please try again later.");
            }
        }
    };
    xhr.send(params);
    return false;
}

Where $(endpoint_url) and $(api_key) are replaced with appropriate values by my setup script (I have confirmed the values are accurate).

This is the verbatim response from the chrome console when the POST request is made:

register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39 OPTIONS https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod post_request @ register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39document.getElementById.onsubmit @ register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:44
register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:1 XMLHttpRequest cannot load https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://s3.amazonaws.com' is therefore not allowed access. The response had HTTP status code 500.
like image 460
Christopher Treadgold Avatar asked Sep 17 '25 17:09

Christopher Treadgold


1 Answers

The put integration of the OPTIONS method needs to have a mapping template which includes a statusCode with a 200 value. It looks like your code was setting the mapping template to an empty string (''). When you create the integration via the API Gateway, it adds a default mapping template of: {"statusCode": 200}

Add the same mapping template to your put integration like so:

# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    type='MOCK',
    requestTemplates={
        'application/json': '{"statusCode": 200}'
    }
)
like image 183
MikeD at AWS Avatar answered Sep 20 '25 09:09

MikeD at AWS