Amplify: CORS header ‘Access-Control-Allow-Origin’ missing error even though CORS is enabled in API Gateway and Lambda headers

Mark Richman picture Mark Richman · Apr 14, 2020 · Viewed 10.7k times · Source

I'm using Amplify, and have my API Gateway proxying to Lambda. I've enabled CORS on my /{proxy+} and deployed the API. In my Lambda function, I'm setting the appropriate header in my trivial function:

import json


def handler(event, context):
    print("received event:")
    print(event)
    return {
        "statusCode": 200,
        "headers": {
            "Access-Control-Allow-Credentials": True,
            "Access-Control-Allow-Headers": "Content-Type",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET",
            "Access-Control-Allow-Origin": "*",
        },
        "body": json.dumps(event),
    }

This Lambda function sits behind an API Gateway resource which is authenticated via Cognito.

When I invoke my API using Amplify:

let myInit = {
          headers: {
            Authorization: `Bearer ${(await Auth.currentSession())
              .getIdToken()
              .getJwtToken()}`
          }
        }; 

API.get("adminapi", "/admin", myInit) ...

I get the dreaded CORS header 'Access-Control-Allow-Origin' missing from my GET request:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/admin. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

I see it returned in the OPTIONS request:

enter image description here

I even tested in Postman to verify the headers are coming back:

enter image description here

What am I doing wrong here? It doesn't look like the call is getting past API Gateway. I wonder if it has to do with authentication. When I test from Postman using my IAM credentials it works fine, but from my web app using the bearer token it fails as above.

Answer

CherryDT picture CherryDT · Apr 14, 2020

I'd assume that you forgot to handle the OPTIONS verb that's used for the preflight request and return the header there.

You are sending an Authorization header, which is not in the list of allowed headers for "simple" requests, hence a preflighted request is done.

Furthermore, in order for credentials to get through, you have to ensure that the Access-Control-Allow-Credentials: true header is set as well.

It's not apparent from the code snippet what content type your request body is, but if it's anything other than application/x-www-form-urlencoded, multipart/form-data or text/plain (say, application/json), you also need to whitelist the Content-Type header using Access-Control-Allow-Headers: Content-Type.