2
votes

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.
1
"mine does not work" does not really give us anything to go on. In what sense does it not work? All I see here is configuration, not examples of observed behavior or descriptions of expected behavior. Have you captured request and response headers with a tool like curl? What were the results? - Michael - sqlbot
Sorry i'm still fairly new at this. I added clarification of what was wrong (500 error) and the output from API Gateway's get-method. Thanks for the feedback :) - Christopher Treadgold
How are you calling the API? If by curl, can you post the verbose request/response details for the OPTIONS call and the POST call. If by a browser, can you post the logs of everything the browser did? (Most browsers have tools that let you see the CORS requests the browser makes and the responses it gets.) - MikeD at AWS
I have added the chrome console output as well as the code used to send the post request. Thanks for looking at my question. - Christopher Treadgold
I haven't been able to find anything yet. Did you remember to re-deploy your API after making your CORS related changes? - MikeD at AWS

1 Answers

2
votes

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}'
    }
)