aws-sam-cli: Problem with OPTIONS requests

Description:

Trying to access my API from a test app in my browser and the OPTIONS request is being handled by SAM CLI API Gateway but it’s not returning the configuration specified in my template file.

The weird part is that if I try to access a different endpoint from the same template it executes the lambda function (as expected since that’s what SAM Local did).

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

Output of sam --version: SAM CLI, version 0.3.0

Optional Debug logs:

> export AWS_SDK_LOAD_CONFIG=true && sam local start-api --docker-network myblueprint  "--debug-port" "5858"

2018-05-10 16:23:38 Mounting HealthCheckFunction at http://127.0.0.1:3000/healthcheck [OPTIONS, GET]
2018-05-10 16:23:38 Mounting EnsureUserFunction at http://127.0.0.1:3000/ensure_user [OPTIONS, PUT]
2018-05-10 16:23:38 Mounting GraphQLServerFunction at http://127.0.0.1:3000/graphql [GET, POST, OPTIONS]
2018-05-10 16:23:38 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2018-05-10 16:23:38  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
2018-05-10 16:24:07 127.0.0.1 - - [10/May/2018 16:24:07] "OPTIONS /graphql HTTP/1.1" 200 -
2018-05-10 16:24:45 Invoking dist/healthcheck.handler (nodejs8.10)
2018-05-10 16:24:45 Found credentials in shared credentials file: ~/.aws/credentials

Fetching lambci/lambda:nodejs8.10 Docker container image......
2018-05-10 16:24:49 Mounting /Users/stefan/Projects/blueprint-api/packages/api as /var/task:ro inside runtime container
Debugger listening on ws://0.0.0.0:5858/698de0ac-e3af-4f27-b5dd-bf3caf4cbc58
For help see https://nodejs.org/en/docs/inspector
Debugger attached.
Debugger listening on ws://0.0.0.0:5858/698de0ac-e3af-4f27-b5dd-bf3caf4cbc58
For help see https://nodejs.org/en/docs/inspector
START RequestId: 2ee985d7-416b-182e-ec4a-79af497b0cc2 Version: $LATEST
2018-05-10T04:25:06.756Z        2ee985d7-416b-182e-ec4a-79af497b0cc2    ---TRIMMED ERROR---
END RequestId: 2ee985d7-416b-182e-ec4a-79af497b0cc2
REPORT RequestId: 2ee985d7-416b-182e-ec4a-79af497b0cc2  Duration: 2838.28 ms    Billed Duration: 2900 ms        Memory Size: 1024 MB    Max Memory Used: 52 MB
2018-05-10 16:25:03 No Content-Type given. Defaulting to 'application/json'.
2018-05-10 16:25:03 127.0.0.1 - - [10/May/2018 16:25:03] "OPTIONS /healthcheck HTTP/1.1" 503 -

Template file for reference

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

Parameters:
  Environment:
    Type: String
    AllowedValues:
      - DEVELOPMENT
      - STAGING
      - PRODUCTION

# DEV SHOULD NEVER BE RUN OUTSIDE OF SAM LOCAL
Mappings:
  Config:
    'DEVELOPMENT':
      EnvironmentLowerCase: 'development'
      HostedZoneName: 'localhost'
      HostedZoneId: 'NONE'
      S3FilesBucket: 'blah'
    'STAGING':
      EnvironmentLowerCase: 'staging'
      HostedZoneName: '<REDACTED>'
      HostedZoneId: '<REDACTED>'
      S3FilesBucket: '<REDACTED>'
    'PRODUCTION':
      EnvironmentLowerCase: 'production'
      HostedZoneName: '<REDACTED>'
      HostedZoneId: '<REDACTED>'
      S3FilesBucket: '<REDACTED>'

Conditions:
  CreateS3FileProcessingResources:
    !Equals [!Ref 'AWS::Region', 'ap-southeast-2']

Globals:
  Function:
      Runtime: 'nodejs8.10'
      MemorySize: 1024
      Timeout: 5
      Tracing: PassThrough
      Environment:
        Variables:
          CODE_ENVIRONMENT: !Ref Environment

Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action: 'sts:AssumeRole'
          Effect: Allow
          Principal:
            Service: 'lambda.amazonaws.com'
      Path: /
      ManagedPolicyArns:
      - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess'
      Policies:
      - PolicyName: s3
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Action:
            - 's3:*'
            Effect: Allow
            Resource: '*'
      - PolicyName: parameters
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - 'ssm:GetParameter'
            - 'ssm:GetParameters'
            - 'ssm:GetParametersByPath'
            Resource:
            - !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${Environment}/API/*'
          - Effect: Allow
            Action:
            - 'kms:Decrypt'
            Resource:
            - !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/*'

  MyBlueprintApi:
    Type: 'AWS::Serverless::Api'
    Properties:
      StageName: 'prod'
      EndpointConfiguration: 'REGIONAL'
      Cors:
        AllowMethods: "'GET,OPTIONS,POST,PUT'"
        AllowHeaders: "'Authorization,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
        AllowOrigin: "'*'"
        MaxAge: "'86400'"
      DefinitionBody:
        swagger: '2.0'
        info:
          version: '0.0.1'
          title:
            'Fn::Sub':
            - 'myblueprint-${EnvironmentLowerCase}'
            - EnvironmentLowerCase: !FindInMap [Config, !Ref Environment, EnvironmentLowerCase]
        basePath: '/prod'
        schemes:
        - 'https'
        paths:
          '/graphql':
            get:
              consumes:
              - 'application/json'
              produces:
              - 'application/json'
              responses:
                '200':
                  description: '200 response'
                  schema:
                    $ref: '#/definitions/Empty'
                  headers:
                    Access-Control-Allow-Origin:
                      type: 'string'
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: '200'
                    responseParameters:
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GraphQLServerFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_templates'
                httpMethod: 'POST'
                contentHandling: 'CONVERT_TO_TEXT'
                type: 'aws_proxy'
            post:
              consumes:
              - 'application/json'
              produces:
              - 'application/json'
              responses:
                '200':
                  description: '200 response'
                  schema:
                    $ref: '#/definitions/Empty'
                  headers:
                    Access-Control-Allow-Origin:
                      type: 'string'
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: '200'
                    responseParameters:
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GraphQLServerFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_templates'
                httpMethod: 'POST'
                contentHandling: 'CONVERT_TO_TEXT'
                type: 'aws_proxy'
            options:
              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,OPTIONS,POST'"
                      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}'
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GraphQLServerFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_match'
                type: 'mock'
          '/ensure_user':
            put:
              consumes:
              - 'application/json'
              produces:
              - 'application/json'
              responses:
                '200':
                  description: '200 response'
                  schema:
                    $ref: '#/definitions/Empty'
                  headers:
                    Access-Control-Allow-Origin:
                      type: 'string'
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: '200'
                    responseParameters:
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EnsureUserFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_templates'
                httpMethod: 'POST'
                contentHandling: 'CONVERT_TO_TEXT'
                type: 'aws_proxy'
            options:
              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: "'PUT,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: "'*'"
                requestTemplates:
                  application/json: '{"statusCode": 200}'
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${EnsureUserFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_match'
                type: 'mock'
          '/healthcheck':
            get:
              consumes:
              - 'application/json'
              produces:
              - 'application/json'
              responses:
                '200':
                  description: '200 response'
                  schema:
                    $ref: '#/definitions/Empty'
                  headers:
                    Access-Control-Allow-Origin:
                      type: 'string'
              x-amazon-apigateway-integration:
                responses:
                  default:
                    statusCode: '200'
                    responseParameters:
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HealthCheckFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_templates'
                httpMethod: 'POST'
                contentHandling: 'CONVERT_TO_TEXT'
                type: 'aws_proxy'
            options:
              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,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: "'*'"
                requestTemplates:
                  application/json: '{"statusCode": 200}'
                uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HealthCheckFunction.Arn}/invocations'
                passthroughBehavior: 'when_no_match'
                type: 'mock'
        definitions:
          Empty:
            type: 'object'
            title: 'Empty Schema'

  GraphQLServerFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: 'dist/graphql.handler'
      CodeUri: './packages/api/'
      Role: !GetAtt LambdaExecutionRole.Arn
      MemorySize: 1536
      Timeout: 30
      Events:
        GetResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/graphql'
            Method: get
        OptionsResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/graphql'
            Method: options
        PostResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/graphql'
            Method: post

  EnsureUserFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: 'dist/ensure_user.handler'
      CodeUri: './packages/api/'
      Role: !GetAtt LambdaExecutionRole.Arn
      Events:
        PutResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/ensure_user'
            Method: put
        OptionsResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/ensure_user'
            Method: options

  S3FileProcessingFunction:
    Type: 'AWS::Serverless::Function'
    Condition: 'CreateS3FileProcessingResources'
    Properties:
      Handler: 'dist/s3files.handler'
      CodeUri: './packages/api/'
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 30
# Cant use this because the bucket must be created in the same template
#      Events:
#        ObjectCreated:
#          Type: S3
#          Properties:
#            Bucket: !FindInMap [Config, !Ref Environment, S3FilesBucket]
#            Events: s3:ObjectCreated:*

  HealthCheckFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: 'dist/healthcheck.handler'
      CodeUri: './packages/api/'
      Role: !GetAtt LambdaExecutionRole.Arn
      Events:
        GetResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/healthcheck'
            Method: get
        OptionsResource:
          Type: Api
          Properties:
            RestApiId: !Ref MyBlueprintApi
            Path: '/healthcheck'
            Method: options
  
  # TODO: update this to use DNS validation when CF supports it
  ApiCertificate:
    Type: 'AWS::CertificateManager::Certificate'
    Properties:
      DomainName:
        'Fn::Sub':
        - 'api.${HostedZoneName}'
        - HostedZoneName: !FindInMap [Config, !Ref Environment, HostedZoneName]
      DomainValidationOptions:
      - DomainName:
          'Fn::Sub':
          - 'api.${HostedZoneName}'
          - HostedZoneName: !FindInMap [Config, !Ref Environment, HostedZoneName]
        ValidationDomain: 'myblueprint.cloud'
  
  CustomResourceLambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action: 'sts:AssumeRole'
          Effect: Allow
          Principal:
            Service: 'lambda.amazonaws.com'
      Path: /
      ManagedPolicyArns:
      - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Policies:
      - PolicyName: ApiGateway
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Action:
            - 'apigateway:*'
            Effect: Allow
            Resource: '*'
  
  DomainNameInfoCustomResourceFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Handler: index.handler
      Role: !GetAtt CustomResourceLambdaExecutionRole.Arn
      Runtime: 'nodejs6.10'
      Timeout: 300
      Code:
        ZipFile: |
          const AWS = require('aws-sdk');
          const response = require('cfn-response');

          exports.handler = function(event, context) {
              const ApiGateway = new AWS.APIGateway();
              ApiGateway.getDomainName({
                  domainName: event.ResourceProperties.DomainName
              }, (err, data) => {
                  if (err != null) {
                      response.send(event, context, response.FAILED, undefined);
                  } else {
                      response.send(event, context, response.SUCCESS, {
                          DomainName: data.domainName,
                          RegionalDomainName: data.regionalDomainName,
                          RegionalHostedZoneId: data.regionalHostedZoneId,
                          DistributionDomainName: data.distributionDomainName,
                          DistributionHostedZoneId: data.distributionHostedZoneId
                      });
                  }
              });
          }

  ApiDomainName:
    Type: 'AWS::ApiGateway::DomainName'
    Properties:
      DomainName:
        'Fn::Sub':
        - 'api.${HostedZoneName}'
        - HostedZoneName: !FindInMap [Config, !Ref Environment, HostedZoneName]
      EndpointConfiguration:
        Types:
        - REGIONAL
      RegionalCertificateArn: !Ref ApiCertificate

  ApiBasePathMapping:
    Type: 'AWS::ApiGateway::BasePathMapping'
    DependsOn: [MyBlueprintApi, ApiDomainName]
    Properties:
      DomainName:
        'Fn::Sub':
        - 'api.${HostedZoneName}'
        - HostedZoneName: !FindInMap [Config, !Ref Environment, HostedZoneName]
      RestApiId: !Ref MyBlueprintApi
      Stage: 'prod'
  
  ApiDomainNameInfo:
    Type: 'Custom::DomainNameInfo'
    DependsOn: [ApiDomainName, ApiBasePathMapping]
    Properties:
      ServiceToken: !GetAtt DomainNameInfoCustomResourceFunction.Arn
      DomainName: !Ref ApiDomainName

  ApiHealthCheck:
    Type: 'AWS::Route53::HealthCheck'
    Properties:
      HealthCheckConfig:
        Port: 443
        Type: 'HTTPS_STR_MATCH'
        SearchString: 'ok'
        ResourcePath: '/prod/healthcheck'
        FullyQualifiedDomainName: !Sub '${MyBlueprintApi}.execute-api.${AWS::Region}.amazonaws.com'
        RequestInterval: 60
        FailureThreshold: 2
      HealthCheckTags:
      - Key: Name
        Value:
          'Fn::Sub':
          - 'api-regional-${EnvironmentLowerCase}-${Region}'
          - EnvironmentLowerCase: !FindInMap [Config, !Ref Environment, EnvironmentLowerCase]
            Region: !Ref 'AWS::Region'

  ApiRecordSet:
    Type: 'AWS::Route53::RecordSet'
    DependsOn: [ApiDomainNameInfo]
    Properties:
      HostedZoneId: !FindInMap [Config, !Ref Environment, HostedZoneId]
      Name:
        'Fn::Sub':
        - 'lbr-api.${HostedZoneName}'
        - HostedZoneName: !FindInMap [Config, !Ref Environment, HostedZoneName]
      ResourceRecords:
      - !GetAtt ApiDomainNameInfo.RegionalDomainName
      Region: !Ref 'AWS::Region'
      SetIdentifier: !Sub 'api-${AWS::Region}'
      HealthCheckId: !Ref ApiHealthCheck
      Type: CNAME
      TTL: 60

Outputs:
  RestAPIID:
    Description: Rest API ID
    Value: !Ref MyBlueprintApi
  ApiUrl:
    Description: URL of your API endpoint
    Value: !Ref ApiRecordSet
  HealthcheckApiUrl:
    Description: URL of your API health check endpoint
    Value: !Sub 'https://${MyBlueprintApi}.execute-api.${AWS::Region}.amazonaws.com/prod/healthcheck'

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 3
  • Comments: 33 (14 by maintainers)

Most upvoted comments

Hi, I’ve got the same issue. Getting 403 for OPTIONS requests to my graphql endpoint for sam local. Is there any work around for this at the moment? CLI version 0.10.0 @jfuss

@rayhaanq Give this a try in your cloudformation yaml

Globals:
  Api:
    # enable CORS; to make more specific, change the origin wildcard
    # to a particular domain name, e.g. "'www.example.com'"
    Cors:
      AllowMethods: "'OPTIONS,GET,POST,PUT,DELETE'"
      AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
      AllowOrigin: "'*'"

Resources:
   OptionsFunction:
    Type: AWS::Serverless::Function
    Properties:
# create a custom handler to return 200 and appropriate headers for your OPTIONS requests
      FunctionName: options-handler
      Handler: index.options #nodejs
      Events:
        Options:
          Type: Api
          Properties:
            Path: /{cors+}
            Method: OPTIONS
            Auth:
              Authorizer: NONE

Finally make it work after a long day suffer from this issue.

It’s a good idea to write another lambda for options method which response successful pre-light check.

I didn’t notice that AWS has mentioned it in their docs [https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html](how to cors)

I am using sam 0.5.0, and I am getting 403 Forbidden for OPTIONS call

2018-08-13 15:10:01 127.0.0.1 - - [13/Aug/2018 15:10:01] "OPTIONS /siteModel/dsmSurface HTTP/1.1" 403 -

Same here, getting 403 for OPTIONS call

SAM CLI, version 0.6.0

Finally, got the root cause here. This is a bug on our end and was pretty difficult to find.

I pulled the head of master locally (equivalent to 0.3.0) and after some long digging found this while inspecting the Flask self._app in service.py.

Pdb) p self._app.url_map
Map([<Rule '/healthcheck' (OPTIONS) -> /healthcheck>,
 <Rule '/ensure_user' (OPTIONS) -> /ensure_user>,
 <Rule '/ensure_user' (PUT, OPTIONS) -> /ensure_user>,
 <Rule '/healthcheck' (HEAD, OPTIONS, GET) -> /healthcheck>,
 <Rule '/graphql' (HEAD, OPTIONS, GET) -> /graphql>,
 <Rule '/graphql' (POST, OPTIONS) -> /graphql>,
 <Rule '/graphql' (OPTIONS) -> /graphql>])

You can see OPTIONS is getting added to every request but there are many rules for each endpoint. The many rules is fine (just caused by the way we define a route). The interesting thing is, why does OPTIONS get appended to every route. Well in Flask 0.8, a provide_automatic_options functionality was added and is on by default. When this is enabled, Flask checks to see if the list of methods pass into add_url_rule has an options method and adds it if it doesn’t. The reason /graphql was only returning a 200 was because of this (Flask will not invoke the _request_handler if this is enabled).

There are a couple things, I think we need to do here:

  1. This would have been addressed if we called add_url_rule once per endpoint, since for the functions that would have added OPTIONS would have worked as expected.
  2. The first only hides the root problem. Since customers defines what endpoints/https verbs they want, we should be disabling this feature in Flask.

@charsleysa I apologize for not going deeper into this originally. The reason I didn’t catch this in the first place was due to not completely keeping the template the same (I scoped this down to make it easier for me to understand but didn’t keep the AWS::Serverless::Function’s identical. The problem is limited to endpoints defined with OPTIONS and other HTTP Verbs. You may not see the issue because it is dependent on the order we add_url_rule (why it worked for /healthcheck but not /graphql.

Updating Labels to reflect current state.

Tracking issue for CORS is #323. See that issue for more details.

The original request here was Flask responding to OPTIONS requests which was solved and release: https://github.com/awslabs/aws-sam-cli/issues/400#issuecomment-398394463

There was addition reports of Flask still responding here: https://github.com/awslabs/aws-sam-cli/issues/400#issuecomment-433442194 but no response from those parties.

Closing at this looks to be solved and other comments relate to CORS support, which has it’s own issue

@cl0ckwork awesome, this worked. I just set up another function for options to return a success response. Thanks

Hi, I’ve got the same issue. Getting 403 for OPTIONS requests to my graphql endpoint for sam local. Is there any work around for this at the moment? CLI version 0.10.0 @jfuss

@andres-lowrie For whatever reason, the Flask version of 1.0.2 wasn’t getting picked up. This is something wrong with the installation on your system. If you are currently using pip to install, I would uninstall the CLI (pip uninstall --user aws-sam-cli and pip3 uninstall --user aws-sam-cli if you have python3 as well) and move to our new installers. This will give you an isolated install of the CLI and ensure the correct versions of all dependencies are install correctly.

@jfuss thanks for your work in finding the problem! That definitely was a hidden one.

@hobotroid thanks for your help as well!

@hobotroid Again there is no CORS support currently. OPTIONS behaves the same as GET or any other of the HTTP verbs. If this is not the case, please give us more details so we can reproduce. As it stands, I have not been able to observe any incorrect behavior, that is when I return a proxy response from a lambda through an OPTIONS verb, I get the correct response as the caller.

@charsleysa Make sure the debugger is setup correctly. If you are using VS code, you need to be setting the debugger to legacy, if I recall correctly. If you are seeing issues with the debugger, please file a separate issue.

As for the CloudFormation Parameter values, are you are needing/wanting to override values in the Env Vars? If so, you can still pass --env-vars to the command. If you are referring to something else, please cut an issue and explain the use-case

@charsleysa Yes, I spent hours trying to get all my OPTIONS rules to work. But at best I could get 75%. It has something to do with the path parsing, but I still can’t figured out exactly what.