skip to Main Content

I have a sam file trying to build an API Gateway to a lambda function. I am following the latest AWS documentation for configuring cors. This documentation is quoted here:

enter image description here

As you may have guessed, that isn’t working for me. If I use "http://localhost:3000" or a variation of that as the "AllowOrigin" property, then I get the following error:

Access to XMLHttpRequest at ‘xxx’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Checking the API gateway, that property is indeed present. Although it seems to be only present for the options and not for the "ANY" section.

enter image description here

If I change that to be AllowOrigin: "’*’", I get the following error:

Access to XMLHttpRequest at ‘xxxx’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘*, *’, but only one is allowed.

In addition to this, there is a separate "Stage" stage created which I don’t understand and I am not sure that the proxy needs to be there or where it is created in the SAM file.

Here is my yaml file for sam.

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Storygraf backend API

Globals:
  Function:
    Timeout: 3

Resources:
  ExpressApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      Cors:
        AllowOrigin: "'*'"
        AllowMethods: "'POST, GET, PUT, DELETE'"
        AllowHeaders: "'X-Forwarded-For, Content-Type'"

  ExpressLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./
      Handler: lambda.handler
      MemorySize: 512
      Runtime: nodejs14.x
      Timeout: 30
      Events:
        ProxyApiRoot:
          Type: Api
          Properties:
            RestApiId: !Ref ExpressApi
            Path: /
            Method: ANY
        ProxyApiGreedy:
          Type: Api
          Properties:
            RestApiId: !Ref ExpressApi
            Path: /{proxy+}
            Method: ANY

Update

It looks like the test works when going through the proxy, but nothing is available when using the non proxy GET command.

enter image description here

enter image description here

Update

I discovered that the package process is changing the quotes, but this doesn’t seem to be the problem.

2

Answers


  1. From your logs your access-control-allow-origin header returned from your GET test returns an array with ['*','*']. It should not be an array and should just be '*'.

    Edit
    enter image description here

    Login or Signup to reply.
  2. This is the template I use, which has a few more things defined. Also I’ve got an API key there, which you can easily remove.

    AWSTemplateFormatVersion: '2010-09-09'
    Transform: AWS::Serverless-2016-10-31
    Description: >
      Storygraf backend API
    
    Resources:
    
      ApiGatewayApi:
        Type: AWS::Serverless::Api
        Properties:
          Name: ExpressApi
          StageName: Prod
          Auth:
            UsagePlan: 
              CreateUsagePlan: PER_API
              UsagePlanName: "Basic Usage Plan"
              Throttle:
                BurstLimit: 5
                RateLimit: 5
            ResourcePolicy:
              CustomStatements: [
                {
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": "execute-api:Invoke",
                  "Resource": "execute-api:/Prod/POST/express"
                },
                {
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": "execute-api:Invoke",
                  "Resource": "execute-api:/Prod/OPTIONS/express"
                }]
          DefinitionBody:
            swagger: '2.0'
            info:
              title: 
                Ref: AWS::StackName
            produces:
              - application/json
            x-amazon-apigateway-api-key-source: "HEADER"
            paths:
              '/express:
                get:
                  security:
                    - ApiKeyAuth: []
                  x-amazon-apigateway-auth:
                    type : "NONE"
                  x-amazon-apigateway-api-key-source: "HEADER"
                  produces:
                    - application/json
                  responses:
                    '200':
                      statusCode: 200
                      description: 200 response
                      schema:
                        $ref: "#/definitions/Empty"
                      headers:
                        Content-Type:
                          type: string
                        Access-Control-Allow-Origin:
                          type: string
                  x-amazon-apigateway-integration:
                    uri: 
                      Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ExpressLambdaFunction.Arn}/invocations"
                    httpMethod: POST //This must always be POST! Even if your method is a GET / PUT etc...
                    type: AWS_PROXY
                    responses:
                      default:
                        statusCode: 200
                        schema:
                          $ref: "#/definitions/Empty"
                        responseParameters:
                          method.response.header.Access-Control-Allow-Origin: "'*'"
                          method.response.header.Content-Type: integration.response.header.content-type
                    passthroughBehavior: when_no_match
                options:
                  x-amazon-apigateway-auth:
                    type : "NONE"
                  consumes:
                    - application/json
                  produces:
                    - application/json
                  responses:
                    '200':
                      description: 200 response
                      schema:
                        $ref: "#/definitions/Empty"
                      headers:
                        Access-Control-Allow-Origin:
                          type: string
                        Access-Control-Allow-Methods:
                          type: string
                        Access-Control-Allow-Headers:
                          type: string
                  x-amazon-apigateway-integration:
                    responses:
                      default:
                        statusCode: 200
                        responseParameters:
                          method.response.header.Access-Control-Allow-Methods: "'GET,POST,OPTIONS'"
                          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-Origin: "'*'"
                    passthroughBehavior: when_no_match
                    requestTemplates:
                      application/json: "{"statusCode": 200}"
                    type: mock
            securityDefinitions:
                ApiKeyAuth:
                  type: apiKey
                  in: header
                  name: x-api-key
            definitions:
                Empty:
                  type: object
                  title: Empty Schema
    
      ExpressLambdaFunction:
        Type: AWS::Serverless::Function
        Properties:
          Timeout: 100
          CodeUri: <relative path to your lambda function>
          Handler: app.lambdaHandler //Assuming method lambdahandler is in app.js
          Runtime: nodejs14.x
          Role: !GetAtt LambdaExecutionRole.Arn
          Events:
            ProxyApiRoot:
              Type: Api
              Properties:
                RestApiId: !Ref ApiGatewayApi
                Path: /express
                Method: get
        
      LambdaExecutionRole:
        Type: AWS::IAM::Role
        Properties:
          AssumeRolePolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Principal: 
                  Service: 
                    - "lambda.amazonaws.com"
                Action: 
                  - "sts:AssumeRole"
          ManagedPolicyArns: 
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
            - <Any other required policy arns>
    
    

    Make sure in your lambda response your headers match those defined in the cloud formation OPTIONS response, for the template I’ve given the response from the lambdaHandler function may look like this (if using js):

    return {
      'statusCode': 200,
      'headers': {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*"
      },
      'body': JSON.stringify({
        "Message": "Successfully did something"
      })
    }
    

    Hope this is somewhat helpful.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search