Ranjit Soni
Ranjit Soni

Reputation: 614

Getting access denied due to origin blocked by CORS in AWS Api-Gateway

I have an AWS SAM template, which creates lambda function and post method in API Gateway. By default, it uses Lambda Proxy integration and it is working fine when I am testing through the PostMan tool but when I am using the API gateway URL with my sandbox app, it is displaying the following error.

Access to XMLHttpRequest at 'https://abcdef.execute-api.eu-west-2.amazonaws.com/dev/my-api' from origin 'https://abcd.csb.app' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

but when I am creating API gateway post method manually and trying then it is working fine.

Lambda function also returning following header in the response.

response = {
        'statusCode': status_code,
        'headers': {
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'OPTIONS,POST'
        },
        'body': json.dumps(response_data)
    }

Following is AWS SAM template.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  AWS SAM Template

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 10

Parameters:
  DeploymentEnv:
    Type: String

Resources:
  ApiGatewayApi:
    DependsOn: LambdaFunction
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref DeploymentEnv
      EndpointConfiguration: 
        Type: REGIONAL
      Cors:
        AllowMethods: "'POST,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
        AllowOrigin: "'*'"
        MaxAge: "'600'"
        AllowCredentials: false
      Auth:
        DefaultAuthorizer: NONE
        ApiKeyRequired: true # sets for all methods
  
  LambdaFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      FunctionName: !Join [ "", [ !Ref DeploymentEnv, "-my-lambda"]]
      CodeUri: my_api/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        EventTriggerlambda:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties: 
            RestApiId: !Ref ApiGatewayApi
            Path: /my-api
            Method: POST
            Auth:
              ApiKeyRequired: true
      Role: Role_URN
      Environment:
        Variables:
          URI: Test
          USER_NAME: Test
          PASSWORD: Test
  
  ApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn: ApiGatewayApiStage
    Properties:
      Name: !Join ["", [{"Ref": "AWS::StackName"}, "-apikey"]]
      Enabled: true
      StageKeys:
        - RestApiId: !Ref ApiGatewayApi
          StageName: !Ref DeploymentEnv
  
  UsagePlan:
    DependsOn: 
      - ApiGatewayApiStage
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref ApiGatewayApi
          Stage: !Ref DeploymentEnv
      Throttle:
        BurstLimit: 500
        RateLimit: 100
      UsagePlanName: MY-UsagePlan
      
  UsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref ApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref UsagePlan

Outputs:
  LambdaFunction:
    Description: "Lambda Function ARN"
    Value: !GetAtt LambdaFunction.Arn

Please help, Thanks :)

Upvotes: 1

Views: 486

Answers (1)

Ranjit Soni
Ranjit Soni

Reputation: 614

Configuration is not proper for API creation in API-Gateway in the AWS SAM template. because SAM deployment uses lambda proxy integration by default that's why in method response, there are few values required which can not be set automatically using the above configuration. So I use open API specification where I defined Rest API configuration and it is working fine without any manual intervention after deployment.

Following configuration is fine.

ApiGatewayApi:
    DependsOn: LambdaFunction
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref DeploymentEnv
      DefinitionBody:
          'Fn::Transform':
            Name: 'AWS::Include'
            Parameters:
              Location: !Join [ '', [ 's3://mybucket', '/openapi-spec.yaml'  ] ]
      EndpointConfiguration: 
        Type: REGIONAL

OpenAPi Configuration

openapi: "3.0.1"
info:
  title: "test-api"
  description: "Created by AWS Lambda"
  version: "2022-01-07T18:00:40Z"

paths:
  /test-api:
    post:
      responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
      x-amazon-apigateway-integration:
        httpMethod: "POST"
        uri:  
          Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Origin: "'*'"
        passthroughBehavior: "when_no_match"
        contentHandling: "CONVERT_TO_TEXT"
        type: "aws_proxy"
    options:
      responses:
        "200":
          description: "200 response"
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: "string"
            Access-Control-Allow-Methods:
              schema:
                type: "string"
            Access-Control-Allow-Headers:
              schema:
                type: "string"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Empty"
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
              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: "'*'"
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        passthroughBehavior: "when_no_match"
        type: "mock"
    x-amazon-apigateway-any-method:
      responses:
        "200":
          description: "200 response"
          content: {}
      security:
      - api_key: []
      x-amazon-apigateway-integration:
        httpMethod: "POST"
        uri: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LambdaFunction.Arn}/invocations"
        responses:
          ".*":
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        type: "aws_proxy"
components:
  schemas:
    Empty:
      title: "Empty Schema"
      type: "object"
  securitySchemes:
    api_key:
      type: "apiKey"
      name: "x-api-key"
      in: "header"

here openapi-spec.yaml file was kept in the same folder as the AWS SAM template and it was uploaded to the S3 bucket before deployment start using the following command in the GitHub workflow pipeline file.

- run: aws s3 cp openapi-spec.yaml s3://mnai-code-deployments
        - run: sam build
        - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --stack-name my-stack --s3-bucket mybucket  --capabilities CAPABILITY_IAM --region eu-west-2 --parameter-overrides ParameterKey=DeploymentEnv,ParameterValue=dev ParameterKey=S3Bucket,ParameterValue=mybucket

Thanks

Upvotes: 2

Related Questions