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

defaultMethodOptions should be selectively applied to CORS OPTIONS method #8615

Closed
whimzyLive opened this issue Jun 18, 2020 · 19 comments · Fixed by #22402
Closed

defaultMethodOptions should be selectively applied to CORS OPTIONS method #8615

whimzyLive opened this issue Jun 18, 2020 · 19 comments · Fixed by #22402
Assignees
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. effort/small Small work item – less than a day of effort in-progress This issue is being actively worked on. p1

Comments

@whimzyLive
Copy link
Contributor

whimzyLive commented Jun 18, 2020

Tl;DR

defaultMethodOptions should exclude options method by default or at least have an option to do that.

Read More

LambdaRestApi has defaultMethodOption, when it is specified, it gets applied to all methods for given api resource. which is awesome but doesn't work when CORS is involved.

Take this simple example,
where I want all the methods to require x-api-key api key so I specify

defaultMethodOption: {
  apiKeyRequired: true
}

On top of this I also want CORS to be enabled on all api resources so in LambdaRestApi I also specify this

defaultCorsPreflightOptions : {
  ... cors options 
} 

Because of defaultCorsPreflightOptions an additional method will be created for all my resources to support CORS but because defaultMethodOption applies on all methods, options will also have apiKeyRequired , which it shouldn't.

Environment

  • CLI Version : 1.38
  • Framework Version: 1.38
  • Node.js Version: 12 -->
  • OS :
  • Language (Version): TypeScript (3.8.3) -->

This is 🐛 Bug Report

@whimzyLive whimzyLive added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jun 18, 2020
@SomayaB SomayaB added the @aws-cdk/aws-apigateway Related to Amazon API Gateway label Jun 18, 2020
@nija-at
Copy link
Contributor

nija-at commented Jun 22, 2020

Thanks for filing this issue @whimzyLive.

The 'OPTIONS' method created as part of CORS preflight setup will have to be adjusted such that only properties in defaultMethodOptions that apply in the CORS context are filtered.

For the time being, you should be able to work around this using escape hatches.

@nija-at nija-at added p2 and removed needs-triage This issue or PR still needs to be triaged. labels Jun 22, 2020
@nija-at nija-at changed the title LambdaRestApi defaultMethodOptions should not be applied to OPTIONS methods defaultMethodOptions should be selectively applied to CORS OPTIONS method Jun 22, 2020
@asterikx
Copy link
Contributor

asterikx commented Jun 26, 2020

I stumbled across this too.

For now, I have removed the API key requirement from the RestApi and added it to each resource instead. This isn't ideal though.

@whimzyLive
Copy link
Contributor Author

@asterikx until issue is resolved correctly, you can use cdk escape hatches as also suggested by @nija-at.

For my case I did

    const corsMethods = this.apiV1.methods.filter(
      method => method.httpMethod === 'OPTIONS'
    );
    corsMethods.forEach(method => {
      const cfnMethod = method.node.defaultChild as CfnMethod;
      cfnMethod.addPropertyOverride('ApiKeyRequired', false);
      cfnMethod.addPropertyOverride('AuthorizationType', 'NONE');
      cfnMethod.addPropertyDeletionOverride('AuthorizerId');
    });

@asterikx
Copy link
Contributor

asterikx commented Jun 27, 2020

@whimzyLive thanks for sharing! I wasn't sure how to do this.

@mhart
Copy link

mhart commented Jul 15, 2020

There are extra issues with this and proxy support AFAICT. For example, using LambdaRestApi:

const api = new apigateway.LambdaRestApi(stack, 'DummyApi', {
  handler,
  defaultMethodOptions: { authorizationType: apigateway.AuthorizationType.IAM },
  defaultCorsPreflightOptions: { allowOrigins: apigateway.Cors.ALL_ORIGINS },
})

api.methods.filter(m => m.httpMethod === 'OPTIONS')
  .forEach(m => m.node.defaultChild.addPropertyOverride('AuthorizationType', 'NONE'))

This works in so much as the OPTIONS methods are changed correctly in the CFN output to not use auth – but I still get 403s when sending OPTIONS requests – and I believe (although happy to be wrong) it's because the auth on the ANY methods from the proxy is overriding the (lack of) auth on the OPTIONS methods on the same resources.

EDIT: this was wrong. I believe the root cause is that the CDK is failing to see the addPropertyOverride changes when calculating the hash for the Deployment – so when the CloudFormation stack is updated after adding overrides, the deployment appears not to have changed, and so CloudFormation doesn't deploy the auth changes (because CloudFormation has no auto-deploy mechanism)

I'm guessing addPropertyOverride changes are out of scope for determining the deployment hash? If not, I'll file a separate issue for that. But definitely something people should be aware of – you'll need to trigger the deployment to change some other way to get the stack to update.

EDIT2: Added an issue for this over at #9086

@mhart
Copy link

mhart commented Jul 15, 2020

I guess this is akin to the AddDefaultAuthorizerToCorsPreflight property in SAM

@mhart
Copy link

mhart commented Jul 15, 2020

Another related issue here (if not exactly the same): #906 (comment)

@whimzyLive
Copy link
Contributor Author

@mhart I am using lambdaRestApi with above workaround and all options are working fine. Only difference is: I am using it with custom Authorizer instead of IAM Authorizer. If you could share a part of your synthesised template that contains AWS::ApiGateway::Method I might be able to help.

@mhart
Copy link

mhart commented Jul 15, 2020

Like I said, I'm pretty sure the generated CFN is actually right – I mean, it's removed the auth from OPTIONS – the problem is that (I suspect) the auth on the ANY method is overriding the non-auth on the OPTIONS method.

Anyway, here are the generated AWS::ApiGateway::Methods:

EDIT: Updated – I only had the two proxy methods in before, now have all four (two proxy + two root):

{
    "DummyApiOPTIONSA607B51F": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "OPTIONS",
        "ResourceId": {
          "Fn::GetAtt": [
            "DummyApi80F1E171",
            "RootResourceId"
          ]
        },
        "RestApiId": {
          "Ref": "DummyApi80F1E171"
        },
        "AuthorizationType": "NONE",
        "Integration": {
          "IntegrationResponses": [
            {
              "ResponseParameters": {
                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
                "method.response.header.Access-Control-Allow-Origin": "'*'",
                "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
              },
              "StatusCode": "204"
            }
          ],
          "RequestTemplates": {
            "application/json": "{ statusCode: 200 }"
          },
          "Type": "MOCK"
        },
        "MethodResponses": [
          {
            "ResponseParameters": {
              "method.response.header.Access-Control-Allow-Headers": true,
              "method.response.header.Access-Control-Allow-Origin": true,
              "method.response.header.Access-Control-Allow-Methods": true
            },
            "StatusCode": "204"
          }
        ]
      }
    },
    "DummyApiproxyOPTIONSC056BEDA": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "OPTIONS",
        "ResourceId": {
          "Ref": "DummyApiproxy55B1C1CD"
        },
        "RestApiId": {
          "Ref": "DummyApi80F1E171"
        },
        "AuthorizationType": "NONE",
        "Integration": {
          "IntegrationResponses": [
            {
              "ResponseParameters": {
                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'",
                "method.response.header.Access-Control-Allow-Origin": "'*'",
                "method.response.header.Access-Control-Allow-Methods": "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
              },
              "StatusCode": "204"
            }
          ],
          "RequestTemplates": {
            "application/json": "{ statusCode: 200 }"
          },
          "Type": "MOCK"
        },
        "MethodResponses": [
          {
            "ResponseParameters": {
              "method.response.header.Access-Control-Allow-Headers": true,
              "method.response.header.Access-Control-Allow-Origin": true,
              "method.response.header.Access-Control-Allow-Methods": true
            },
            "StatusCode": "204"
          }
        ]
      }
    },
    "DummyApiproxyANY1EBD4910": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "ANY",
        "ResourceId": {
          "Ref": "DummyApiproxy55B1C1CD"
        },
        "RestApiId": {
          "Ref": "DummyApi80F1E171"
        },
        "AuthorizationType": "AWS_IAM",
        "Integration": {
          "IntegrationHttpMethod": "POST",
          "Type": "AWS_PROXY",
          "Uri": {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":apigateway:",
                {
                  "Ref": "AWS::Region"
                },
                ":lambda:path/2015-03-31/functions/",
                {
                  "Fn::GetAtt": [
                    "DummyFunction3BB5AE03",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        }
      }
    },
    "DummyApiANYCEB66499": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "ANY",
        "ResourceId": {
          "Fn::GetAtt": [
            "DummyApi80F1E171",
            "RootResourceId"
          ]
        },
        "RestApiId": {
          "Ref": "DummyApi80F1E171"
        },
        "AuthorizationType": "AWS_IAM",
        "Integration": {
          "IntegrationHttpMethod": "POST",
          "Type": "AWS_PROXY",
          "Uri": {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":apigateway:",
                {
                  "Ref": "AWS::Region"
                },
                ":lambda:path/2015-03-31/functions/",
                {
                  "Fn::GetAtt": [
                    "DummyFunction3BB5AE03",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        }
      }
    }
  }
}

@whimzyLive
Copy link
Contributor Author

whimzyLive commented Jul 15, 2020

@mhart Okay I see you are using Lambda proxy integration in particular. For Lambda proxy
your backend is responsible for returning the Access-Control-Allow-Origin and Access-Control-Allow-Headers headers, because a proxy integration doesn't return an integration response. - from docs

This is mentioned here, under Enabling CORS support for Lambda or HTTP proxy integrations.

@mhart
Copy link

mhart commented Jul 15, 2020

That doesn't make sense – the 403 happens before the Lambda is even invoked.

@whimzyLive
Copy link
Contributor Author

This was the last thing I could think of. If you are getting 403 on options itself then we might have had different issue. Let me know if you find out something.

@mhart
Copy link

mhart commented Jul 15, 2020

Found the issue – it was a complicated scenario with CFN stack updates and CDK deployment hashes not being updated. More info in the updated comment here: #8615 (comment)

@nija-at nija-at added effort/medium Medium work item – several days of effort effort/small Small work item – less than a day of effort and removed effort/medium Medium work item – several days of effort labels Aug 10, 2020
@jimfum
Copy link

jimfum commented Oct 19, 2021

I also have the above issue and have come up with the same CfnMethod workaround. Couldn't believe there isn't anything better, anything official without Cfn and that maybe defaultMethodOptions isn't popular enough(?).

SAM has a solution for it though AddDefaultAuthorizerToCorsPreflight (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html).

@leantorres73
Copy link

I also have the above issue and have come up with the same CfnMethod workaround. Couldn't believe there isn't anything better, anything official without Cfn and that maybe defaultMethodOptions isn't popular enough(?).

SAM has a solution for it though AddDefaultAuthorizerToCorsPreflight (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html).

and how did you fix it? I'm having issues also

@jimfum
Copy link

jimfum commented Nov 2, 2021

Something like the below in TypeScript. Omitting referenced variables but hope it's clear.

const api = new apiGateway.LambdaRestApi(
  this,
  "ABCD",
  {
    restApiName: apiName,
    handler: mainFunc,
    domainName: {
      domainName: apiDomain,
      certificate,
    },
    defaultCorsPreflightOptions: {
      allowOrigins: corsAllowedOrigins,
    },
    defaultMethodOptions: { authorizer: authorizer },
  }
);

api.methods
  .filter((method) => method.httpMethod === "OPTIONS")
  .forEach((method) => {
    const methodCfn = method.node.defaultChild as apiGateway.CfnMethod;
    methodCfn.authorizationType = apiGateway.AuthorizationType.NONE;
    methodCfn.authorizerId = undefined;
  });

@github-actions github-actions bot added p1 and removed p2 labels Aug 28, 2022
@github-actions
Copy link

This issue has received a significant amount of attention so we are automatically upgrading its priority. A member of the community will see the re-prioritization and provide an update on the issue.

@corymhall corymhall self-assigned this Oct 5, 2022
@mergify mergify bot closed this as completed in #22402 Oct 12, 2022
mergify bot pushed a commit that referenced this issue Oct 12, 2022
When you create a RestApi and you provide `defaultCorsPreflightOptions` we automatically create a CORS OPTIONS method for each method. If you also provide `defaultMethodOptions` then those default options get passed through to the CORS OPTION method as well. In the case of authentication options this should not be the case.

This PR explicitly sets the authentication related options to NONE values which overrides whatever is provided in `defaultMethodOptions`.

I've updated an integration tests to assert that an OPTIONS call is successful (I also tested before the fix to assert that it failed).

fixes #8615


----

### All Submissions:

* [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@markcarroll
Copy link

@corymhall please re-open. See comment in #22402 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-apigateway Related to Amazon API Gateway bug This issue is a bug. effort/small Small work item – less than a day of effort in-progress This issue is being actively worked on. p1
Projects
None yet
9 participants