Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS::Serverless::HttpApi CorsConfiguration not working #2637

Open
kstro21 opened this issue Feb 19, 2021 · 9 comments
Open

AWS::Serverless::HttpApi CorsConfiguration not working #2637

kstro21 opened this issue Feb 19, 2021 · 9 comments

Comments

@kstro21
Copy link

kstro21 commented Feb 19, 2021

Description:

I have an AWS::Serverless::HttpApi resource with the CorsConfiguration property. The template deploys ok and I can get a successful response from the endpoint sending a GET request using Postman but, an OPTION request returns error 404.

Steps to reproduce:

Here is my template

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  EnvironmentName:
    Description: An environment name
    Type: String
    Default: production
    AllowedValues:
      - sandbox
      - production
      - staging

  StageName:
    Type: String
    Description: Api Stage Name
    Default: 'v0'

  UiUserPoolId:
    Type: String
    Description: Ui User Pool Id.

  UiUserPoolClientId:
    Type: String
    Description: Ui User Pool Client Id.

Globals:
  Function:
    Runtime: nodejs12.x
    MemorySize: 128
    Timeout: 60
    Handler: index.handler
    Environment:
      Variables:
        AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1

Resources:
  MyApi:
    Type: 'AWS::Serverless::HttpApi'
    Properties:
      StageName: !Ref 'StageName'
      CorsConfiguration:
        AllowCredentials: true
        AllowMethods: "'GET,POST,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigins: "'http://localhost:9000'"
      Auth:
        Authorizers:
          OAuth2Authorizer:
            JwtConfiguration:
              issuer: !Sub 'https://cognito-idp.${AWS::Region}.amazonaws.com/${UiUserPoolId}'
              audience:
                - !Ref UiUserPoolClientId
            IdentitySource: '$request.header.Authorization'
      Tags:
        Environment: !Sub '${EnvironmentName}'

  FantazGlobalNotificationFunction:
    Type: AWS::Serverless::Function
    Properties:
      Description: global notification for FantaZ UI
      FunctionName: !Sub '${EnvironmentName}-client-global-notification-lambda'
      CodeUri: ./global-notification/
      MemorySize: 128
      Environment:
        Variables:
          GLOBAL_NOTIFICATION_TABLE: !Sub '${EnvironmentName}-cf-GlobalNotification'
          ORIGIN: 'http://localhost:9000'
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Sub '${EnvironmentName}-cf-GlobalNotification'
      Events:
        Get:
          Type: HttpApi
          Properties:
            Path: /globalNotification
            ApiId: !Ref MyApi
            Method: GET
      Tags:
        Environment: !Sub '${EnvironmentName}'

Outputs:
  MyApiUrl:
    Value: !Sub 'https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}'
    Description: Client API URL

Observed result:

After deploying the template, if I go to the API Gateway console and open the CORS page this is what I see
image

As you can see, there is no value set. If I send a GET to /v0/globalNotification it works ok, but sending an OPTIONS to the same endpoint fails with a 404 error.
image

Am I missing something? Is my template wrong?

Expected result:

Send an OPTIONS request works as expected.

Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

  1. OS: Linux
  2. sam --version: 1.18.1
  3. AWS region: us-east-1
@sriram-mv
Copy link
Contributor

sriram-mv commented Feb 21, 2021

I have not gone into full detail on this, but from preliminary glance: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html#sam-httpapi-corsconfiguration

AWS SAM requires that DefinitionBody be provided with openAPI definition for CORs to work.

Closing this issue, please reach out if this does not solve it.

@kstro21
Copy link
Author

kstro21 commented Feb 22, 2021

Thanks, @sriram-mv for your response but, no, that does not help at all. I saw that doc already and I have to say it doesn't help either. It only mentions CORS works only if the DefinitionBody property is specified but, what is the min required inside DefinitionBody to make it work? Where can I find an example? There is no one blogging about it :-)

I've tried this

Resources:
  MyApi:
    Type: 'AWS::Serverless::HttpApi'
    Properties:
      StageName: !Ref 'StageName'
      DefinitionBody:
        openapi: 3.0.1
        info:
          title: !Ref 'AWS::StackName'
        paths: {}
      CorsConfiguration:
        AllowCredentials: true
        AllowMethods: "'GET,POST,OPTIONS'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigins: "'http://localhost:9000'"
      Auth:
        Authorizers:
          OAuth2Authorizer:
            JwtConfiguration:
              issuer: !Sub 'https://cognito-idp.${AWS::Region}.amazonaws.com/${UiUserPoolId}'
              audience:
                - !Ref UiUserPoolClientId
            IdentitySource: '$request.header.Authorization'
      Tags:
        Environment: !Sub '${EnvironmentName}'

As you can see, I'm specifying the DefinitionBody property but it doesn't work either.

And if I have to configure the x-amazon-apigateway-cors property inside DefinitionBody, what is the point of having the CorsConfiguration property? Am I missing something here or there is something not working as it should or, probably, the documentation is not enough?

@rumsrami
Copy link

rumsrami commented Apr 4, 2021

This is very frustrating, after spending 2 days battling with documentation and getting things setup, i get hit by this. This is called an httpAPI this should be a straight forward thing to do. I will be abandoning SAM CLI due to this mess.

@hoffa hoffa reopened this Apr 5, 2021
@mingkun2020 mingkun2020 added the stage/bug-repro The issue/bug needs to be reproduced label Apr 5, 2021
@iongion
Copy link

iongion commented Jun 4, 2021

Is the new HttpApi ready for production ? Is not only this that is not working, following all proper docs available online, I can't get anything working

  • The CORS configuration is not respected
  • The JWT mechanism ends up with Internal Server Error and no further details
  • No calls are ever made to the JWT Authorizer lambda function, I see no log group created
  • If I disable the custom authorizer, the sample service gets called, log group gets created, as long as you don't pass the X-Api-Key header, which ends up with the Internal Server Error above
  • Still, even in the case above, the CORS configuration is not respected

Don't know what else I can do

{
    "stage": "dev",
    "requestTime": "03/Jun/2021:09:56:21 +0000",
    "sourceIp": "85.201.8.224",
    "protocol": "HTTP/1.1",
    "status": "500",
    "httpMethod": "GET",
    "requestId": "AV8S6j-SDoEEP9A=",
    "routeKey": "GET /api/sequences/{sequence_id}",
    "path": "/dev/api/sequences/sdsd",
    "responseLength": "35",
    "integration.status": "-",
    "integration.error": "-",
    "authorizer.error": "-",
    "authorizer.integrationStatus": "-",
    "authorizer.status": "-",
    "integrationErrorMessage": "-",
    "error.message": "Internal Server Error"
}
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: DataSet HTTP API and helper utilities

Parameters:
  StageName:
    Type: String
    Default: dev
    Description: (Required) Enter dev, test, stag, prod. Default is dev.
    AllowedValues:
      - dev
      - test
      - stag
      - prod

Globals:
  Function:
    Timeout: 600
    MemorySize: 512
    Runtime: nodejs12.x
    Environment:
      Variables:
        RUNTIME: online
        STAGE: !Ref StageName
    CodeUri: DataSet/
  Api:
    OpenApiVersion: '3.0.1'
    # See https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapi
    BinaryMediaTypes:
      - "*~1*"

Resources:

  DataSetApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: !Ref StageName
      # FailOnWarnings: True
      DefaultRouteSettings:
        DetailedMetricsEnabled: true
      AccessLogSettings:
        DestinationArn: !GetAtt DataSetApiAccessLogs.Arn
        # See https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging-variables.html
        Format: '{"stage":"$context.stage","requestTime":"$context.requestTime","sourceIp":"$context.identity.sourceIp","protocol":"$context.protocol","status":"$context.status","httpMethod":"$context.httpMethod","requestId":"$context.requestId","routeKey":"$context.routeKey","path":"$context.path","responseLength":"$context.responseLength","integration.status":"$context.integration.status","integration.error":"$context.integration.error","authorizer.error":"$context.authorizer.error","authorizer.integrationStatus":"$context.authorizer.integrationStatus","authorizer.status":"$context.authorizer.status","integrationErrorMessage":"$context.integrationErrorMessage","error.message":"$context.error.message"}'
      Auth:
        DefaultAuthorizer: JWTCustomAuthorizer
        Authorizers:
          JWTCustomAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn: !GetAtt DataSetJWTAuthorize.Arn
            FunctionInvokeRole: !GetAtt DataSetJWTAuthorizeRole.Arn
            Identity:
              Headers:
                - X-Api-Key
            AuthorizerPayloadFormatVersion: 2.0
            EnableSimpleResponses: true

    CorsConfiguration:
      AllowOrigins:
        - '*'
      AllowHeaders:
        - X-Api-Key
      AllowMethods:
        - OPTIONS
        - HEAD
        - GET
        - POST
        - PUT
        - DELETE
        - PATCH
      MaxAge: 600
      AllowCredentials: true

  DataSetApiAccessLogs:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join ["-", ["DataSetApiLogGroup", !Ref StageName]]
      RetentionInDays: 30

  # See https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction

  DataSetSequencesGetAllFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: services/DataSetSequencesGetAll.lambdaHandler
      Events:
        DataSetSequencesGetAll:
          Type: HttpApi
          Properties:
            ApiId: !Ref DataSetApi
            Path: /api/sequences
            Method: get
            PayloadFormatVersion: "2.0"

  DataSetJWTCreateFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: services/DataSetJWTCreate.lambdaHandler
      Events:
        DataSetJWTCreate:
          Type: HttpApi
          Properties:
            ApiId: !Ref DataSetApi
            Path: /api/jwt
            Method: post
            PayloadFormatVersion: "2.0"
            Auth:
              Authorizer: "NONE"

  DataSetJWTAuthorize:
    Type: AWS::Serverless::Function
    Properties:
      Handler: services/DataSetJWTAuthorize.lambdaHandler
      Runtime: nodejs12.x

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  DataSetApiUrl:
    Description: "API Gateway endpoint URL for the DataSet API"
    Value: !Sub "https://${DataSetApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${StageName}/"
  DataSetApiId:
    Description: Api id of DataSetApi
    Value:
      Ref: DataSetApi

If of any help, the authorizer lambda code for my test is also as documented here https://aws.amazon.com/blogs/compute/introducing-iam-and-lambda-authorizers-for-amazon-api-gateway-http-apis/

const createResponse = async (input) => {
  console.debug('Create response from input started');
  return Promise.resolve({
    isAuthorized: true,
    context: {},
  });
};

const executor = async (event, context) => {
  console.debug('Event processing started', { event, context });
  const response = await createResponse({ event, context });
  console.debug('Event processing completed, responding', response);
  return response;
};

exports.lambdaHandler = executor;

@kakudaisuke
Copy link

kakudaisuke commented Oct 19, 2021

I faced the similar problem but in my case, followings setup in template.yaml and Lambda function resolved the CORS in SAM with HTTP API. Hoping that would give you a hint to resolve your problems, I share it.

Resources:
  HelloWorldApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      DefinitionBody:
        openapi: 3.0.1
        info:
          title: !Ref 'AWS::StackName'
        paths: {}
      CorsConfiguration:
        AllowOrigins:
          - "*"
        AllowCredentials: true
        AllowMethods: 
          - POST
        AllowHeaders:
          - Content-Type
          - X-CSRF-TOKEN

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Join
        - '-'
        - - hello_world
          - !Ref Stage
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: ruby2.7
      Events:
        HelloWorld:
          Type: HttpApi
          Properties:
            ApiId: !Ref MyApi
            Path: /hello_world
            Method: post
def lambda_handler(event:, context:)

  { 
    statusCode: 200,
    body: {
      message: 'hello_world'
    }.to_json,
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": true,
      "Access-Control-Allow-Methods": "POST",
      "Access-Control-Allow-Headers": "Content-Type,X-CSRF-TOKEN",
    }
  }

end

On the other hand, I agree that the settings of HTTP API are a bit confusing...

@gauntface
Copy link

For anyone seeing this page because of the issues described in this thread. I wasn't seeing the AllowHeaders configuration being applied by SAM. Changing how I defined the CorsConfiguration got things working.

From:

Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: "focus-api"
      CorsConfiguration:
        AllowOrigins:
          - "http://localhost:1234"
        AllowHeaders: "Authorization"

To:

Resources:
  ApiGatewayApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      StageName: "focus-api"
      CorsConfiguration:
        AllowOrigins:
          - "http://localhost:1234"
        AllowHeaders:
          - "Authorization"

Notice AllowHeaders is now a list rather than just a string. With this, I could see Access-Control-Allow-Headers in the cURL response.

$ curl 'http://localhost:6666/example' \
  -X 'OPTIONS'  -i 
HTTP/1.0 200 OK
Access-Control-Allow-Origin: http://localhost:1234
Access-Control-Allow-Methods: DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT
Access-Control-Allow-Headers: Authorization

Thank you @kakudaisuke for sharing your config, helped me spot this.

@ruminize
Copy link

ruminize commented Jul 10, 2023

As of SAM CLI 1.9

I can confirm that LIST for CorsConfiguration is the way to go.

My super secure CORS config.

  CorsConfiguration:
    MaxAge: 600
    AllowMethods:
      - GET
      - POST
      - DELETE
      - OPTIONS
      - HEAD
      - '*'
      - PUT
      - PATCH
    AllowHeaders:
      - "*"
    AllowOrigins:
      - "*"
    ExposeHeaders:
     - "*"

Now I'm on to figuring out why Authorization is not showing up....

Confirming that also Authorizer Audience is this same way. As in...
Auth:
Authorizers:
TestAuth:
IdentitySource: $request.header.Authorization
JwtConfiguration:
Issuer: https://cognito-idp.us-east-1.amazonaws.com/my-cognito-user-pool-id
Audience:
- my-cognito-client-id
DefaultAuthorizer: TestAuth

Now both CorsConfig and Authorizer show up!!! Thanks for posting, this really helped me out.

Perhaps the real issue, and one that I keep finding, is that there are so many changes to AWS SAM and AWS Resource's that are not reflected in the documentation yet or many of the posts that are written by AWS as well as many developers in the community. This condition certainly creates lot's of frustration. However, the prize is worth it.

@truggeriaws
Copy link

The Aws documentation for CORS Configuration shows that the Allow* fields are of type List and has a note about the DefinitionBody requirement,

NOTE: Cors requires SAM to modify your OpenAPI definition, so it only works with inline OpenApi defined in the DefinitionBody property.

I believe the documentation label can be removed.

@denisbochon
Copy link

I had the same issue with CORS and after digging for a while I have tested the same configuration without OPTIONS method allowed which is used for preflight and it worked. hope this helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests