Search code examples
amazon-web-servicescorsaws-api-gatewayaws-samaws-sam-cli

CORS Request to API Gateway not working in browser


Alright, I'm ready to look very silly. I'd bet money this is a simple problem, I'm just not seeing what I am doing wrong.

I recently deployed a SAM application that sets up an API Gateway. Relevant configuration as followed

# SAM Template Headers + Some Parameters; nothing relating to this.
Resources:

  # The Api Gateway necessary for the authorizers.
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref Stage
      Cors: "'*'"
      Auth:
        DefaultAuthorizer: MyCognitoAuthorizer
        Authorizers:
          MyCognitoAuthorizer:
            UserPoolArn: !ImportValue  UserPoolArn
  # some other functions, but one function should do it. 
  # Verified that all functions are referencing the custom ApiGateway resource.
  PutTodosFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/add.putItemHandler
      Description: Adds a todo.
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref TodosTable
      Environment:
        Variables:
          TODOS_TABLE: !Ref TodosTable
          NODE_ENV: !If [isProd, 'production', 'development']
          REGION: !Ref AWS::Region
      Events:
        PutTodo:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGateway
            Path: /
            Method: PUT

And here's where I feel like I'm going crazy. As you can see, my API Gateway uses AWS Cognito for authorization, so I was testing the flow using the Insomnia desktop application.

All requests work fine, so long as I remember to supply the necessary token. I figured I was golden until I started trying to make the same requests through the browser.

Now I get errors like the following when trying to make the request:

Access to fetch at 'https://xpquux202j.execute-api.us-west-2.amazonaws.com/alpha/' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The code calling this has gone through some iterations, but the most recent is:

const headers = new Headers();
    headers.append('Authorization', 'Bearer <JWT Token Goes Here>');

    fetch(API_URL, {
      headers,
      method: 'options', // have tried put/get as well. Neither goes through.
      mode: 'cors' // tried cors and no-cors
    })
      .then(console.log)
      .catch(console.log);

Originally with the error I thought something on the API Gateway side was misconfigured, but making an OPTIONS request to the endpoint (in Insomnia) gives:

OPTIONS Response Image

And testing it in Results from OPTIONS request through API Gateway

TYIA for any help; I'm sure it's something small that I'm missing I'm sort of tired of trying to figure this out.


Solution

  • Alright so the original question is resolved... sort of.

    Tl;Dr you can use the AddDefaultAuthorizerToCorsPreflight option in the Auth key of the AWS::Serverless::Api call, e.g.

      ApiGateway:
        Type: AWS::Serverless::Api
        Properties:
          StageName: !Ref Stage
          Cors: "'*'"
          Auth:
            DefaultAuthorizer: MyCognitoAuthorizer
            AddDefaultAuthorizerToCorsPreflight: false
            Authorizers:
              MyCognitoAuthorizer:
                UserPoolArn: !ImportValue  UserPoolArn
    

    however, this creates another CORS related issue. When you create the API Gateway with this setup the OPTIONS request does not have the Access-Control-Allow-Headers header in its response. I'm still waiting to hear back from the AWS Sam team about it on the Github issue here: https://github.com/aws/serverless-application-model/issues/2136#issuecomment-915708356

    So far the work around is to simply enable CORS from the console, which will then have the necessary Authorization headers. Obviously not great, but also not particularly game breaking either.

    Update

    After some digging, I've found that making these changes gets the API Gateway to deploy with no errors. The CORS section needs to look like this in the ApiGateway resource.

      ApiGateway:
        Type: AWS::Serverless::Api
        Properties:
          StageName: !Ref Stage
          Cors:
            AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
            AllowMethods: "'*'"
            AllowOrigin: "'*'"
          # same Auth: definition as above.