resource.addCorsPreflight(options)
AllowOrigin
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)AllowHeaders
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers) with defaults for API GatewayAllowMethods
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods)AllowCredentials
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials)MaxAge
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age)ExposeHeaders
](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)Vary
response headerLambdaRestApi
(apply a CORS policy to all routes)AWS::ApiGateway::GatewayResponse
Coming from stack overflow
Note that we get a lot of confusion around this since it only configures the Preflight request. Customers expect it to be a magic setting for enabling CORS headers on their Lambda responses. We cant do this because API Gateway does not allow response header mapping for Lambda proxy. Might be better to call this CorsPreflight? At minimum document it clearly.
From @kennu:
function addCorsOptions(apiResource: apigateway.IRestApiResource) {
const options = apiResource.addMethod('OPTIONS', new apigateway.MockIntegration({
integrationResponses: [{
statusCode: '200',
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-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,GET,PUT,POST,DELETE'",
},
}],
passthroughBehavior: apigateway.PassthroughBehavior.Never,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
},
}))
const methodResource = options.findChild('Resource') as apigateway.cloudformation.MethodResource
methodResource.propertyOverrides.methodResponses = [{
statusCode: '200',
responseModels: {
'application/json': 'Empty'
},
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true,
'method.response.header.Access-Control-Allow-Origin': true,
},
}]
}
I am also currently struggling with this. How do you actually use the provided code snippet? Add it to the root of the api? Or for every Resource I want to enable CORS for? I currently have something like:
const blog_api = new apigw.RestApi(this,"Blog-Rest-Api");
const blogs = blog_api.root.addResource('blogs');
const blog = blogs.addResource('{blog_id}');
const addBlog = blogs.addResource('add');
// A get to blogs just returns all blogs
const getBlogsIntegration = new apigw.LambdaIntegration(getBlogsHandler);
blogs.addMethod('GET',getBlogsIntegration);
// A get to blogs/id gets only the one blog with the specific id
const getBlogIntegration = new apigw.LambdaIntegration(getBlogHandler);
blog.addMethod('GET',getBlogIntegration);
With what variables to I have to call addCorsOptions and at which point? I tried on the root and on blogs, but couldn't get it to work yet.
I have attached it to the API root and any resource paths like this:
const api = new apigateway.RestApi(this, 'Api', { ... })
addCorsOptions(api.root)
const apiContacts = api.root.addResource('contacts')
addCorsOptions(apiContacts)
You should be able to verify after deployment in API Gateway Console that everything is present.
But you also need to return an Access-Control-Allow-Origin
header from your Lambda, because API Gateway doesn't add that automatically to the responses. The addCorsOptions()
function here only adds a separate OPTIONS method and its headers.
(I do wish API Gateway could handle all of this automagically with a single enable option.. It's been very complicated from the beginning.)
Thanks, that worked!
Example provided by @kennu stopped working for the latest version of the CDK. However, after some changes following code is working for me:
function addCorsOptions(apiResource: apigateway.IRestApiResource) {
const options = apiResource.addMethod('OPTIONS', new apigateway.MockIntegration({
integrationResponses: [{
statusCode: '200',
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-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,GET,PUT,POST,DELETE'",
},
}],
passthroughBehavior: apigateway.PassthroughBehavior.Never,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
},
}))
const methodResource = (options as cdk.Construct).node.findChild("Resource") as apigateway.CfnMethod
methodResource.propertyOverrides.methodResponses = [{
statusCode: '200',
responseModels: {
'application/json': 'Empty'
},
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true,
'method.response.header.Access-Control-Allow-Origin': true,
},
}]
}
As a hint if you get 500 status on OPTION
methods is to set following in the MockIntegration
definition (it is a common error if you are using compression over API Gateway):
contentHandling: apigateway.ContentHandling.ConvertToText
Hello, I am trying to implement something similar, but in Java. I unfortunately cannot get past the const methodResource = (options as cdk.Construct).node.findChild("Resource") as apigateway.CfnMethod
piece of this code.
The Java implementation is not letting me cast the cdk.Construct
to an apigateway.CfnMethod
.
Any Java experts out there who might be able to lend a hand?
Thanks in advance!
I can't help but I am looking forward to awslabs/aws-cdk#1572 be resolved and merged.
@piotrkubisa thank you for the note and reference to the issue. I'll be keeping an eye out for that issue as well!
The propertyOverrides.methodResponses
option no longer works after the refactor: readonly struct properties and hide internals PR.
I'm not really sure how to work around this now.
Any ideas?
I think you can now specify the method responses as an option to addMethod()
export function addCorsOptions(apiResource: apigateway.IRestApiResource) {
apiResource.addMethod('OPTIONS', new apigateway.MockIntegration({
integrationResponses: [{
statusCode: '200',
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-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,GET,PUT,POST,DELETE'",
},
}],
passthroughBehavior: apigateway.PassthroughBehavior.Never,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
},
}), {
methodResponses: [{
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true,
'method.response.header.Access-Control-Allow-Origin': true,
},
}]
})
}
Oh nice one, thanks :thumbsup:
This is not working. Broken in 0.37.0 ?
What is not working? What is the error you are seeing?
Just a few edits:
export function addCorsOptions(apiResource: apigateway.IResource) {
apiResource.addMethod('OPTIONS', new apigateway.MockIntegration({
integrationResponses: [{
statusCode: '200',
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-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'OPTIONS,GET,PUT,POST,DELETE'",
},
}],
passthroughBehavior: apigateway.PassthroughBehavior.NEVER,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
},
}), {
methodResponses: [{
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true,
'method.response.header.Access-Control-Allow-Origin': true,
},
}]
})
}
Works like a charm
Just curious why it's MockIntegration. It feels weird to put that in a production config
@eladb I'm using LambdaRestApi and when I try to add this - it simply remove the lambda integration.... how to add CORS in this case ?
const APIGW = new apigw.LambdaRestApi(this, 'ListBranchesAPIGW', {
handler: FUNCTION,
apiKeySourceType: apigw.ApiKeySourceType.HEADER,
restApiName: 'ListBranchesAPI',
deployOptions: { stageName: 'prod' },
defaultMethodOptions: { apiKeyRequired: true },
proxy: true
});
addCorsOptions(APIGW.root)
thanks every one on this thread for the code samples. Finally, I successfully, deployed an API Gateway + Lambda Proxy with COGNITO Authorizer - sharing the code - might help someone who lands here..
// construct: lambda
const FUNCTION = new lambda.Function(this, 'ListBranchesHandler', {
runtime: lambda.Runtime.NODEJS_10_X,
code: lambda.Code.asset('lib/list-branches/function'), // your function directory
handler: 'list-branches.handler'
});
// construct: apigw
const APIGW = new apigw.LambdaRestApi(this, 'ListBranchesAPIGW', {
handler: FUNCTION, // attaching lambda function
apiKeySourceType: apigw.ApiKeySourceType.HEADER,
restApiName: 'ListBranchesAPI',
deployOptions: { stageName: 'prod' },
defaultMethodOptions: {
apiKeyRequired: false
},
proxy: false
});
// --> defining an authorizer COGNITO
const authorizer = new CfnAuthorizer(this, 'CognitoAuth', {
restApiId: APIGW.restApiId.toString(),
type: apigw.AuthorizationType.COGNITO,
identitySource: 'method.request.header.Authorization',
name: 'CognitoAuthName',
providerArns: ['ARN OF COGNITO USER POOL']
})
// --> defining an authorizer COGNITO
const resource = APIGW.root.addResource('branches') // creating a resource
addCorsOptions(resource) // adding OPTION for CORS
// --> adding the authorizer
const method = resource.addMethod('GET', undefined, {
// apiKeyRequired: true, // enable this if you need API key
authorizationType: apigw.AuthorizationType.COGNITO,
authorizer: {authorizerId: authorizer.ref}
})
// --> adding the authorizer
function addCorsOptions(apiResource: apigw.IResource) {
apiResource.addMethod('OPTIONS', new apigw.MockIntegration({
integrationResponses: [{
statusCode: '200',
// contentHandling: apigw.ContentHandling.CONVERT_TO_TEXT,
responseParameters: {
'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': "'*'",
'method.response.header.Access-Control-Allow-Credentials': "'false'",
'method.response.header.Access-Control-Allow-Methods': "'GET,OPTIONS'", // modify this based on methods
}
}],
passthroughBehavior: apigw.PassthroughBehavior.WHEN_NO_MATCH,
requestTemplates: {
"application/json": "{\"statusCode\": 200}"
}
}), {
methodResponses: [{
statusCode: '200',
responseParameters: {
'method.response.header.Access-Control-Allow-Headers': true,
'method.response.header.Access-Control-Allow-Methods': true,
'method.response.header.Access-Control-Allow-Credentials': true, // COGNITO
'method.response.header.Access-Control-Allow-Origin': true,
}
}]
})
}
thanks every one on this thread for the code samples. Finally, I successfully, deployed an API Gateway + Lambda Proxy with COGNITO Authorizer - sharing the code - might help someone who lands here..
// --> defining an authorizer COGNITO const authorizer = new CfnAuthorizer(this, 'CognitoAuth', { restApiId: APIGW.restApiId.toString(), type: apigw.AuthorizationType.COGNITO, identitySource: 'method.request.header.Authorization', name: 'CognitoAuthName', providerArns: ['ARN OF COGNITO USER POOL'] })
@smfontevan, this is super helpful. I have a CFn stack where I assign a function to each resource's method (or at least to each resource), but the [LambdaRestApi
] construct (https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_apigateway/LambdaRestApi.html) wasn't getting me there. I don't work with API Gateway a lot, so seeing your JS code was helpful. While converting to Python, the Cognito authorizer above wasn't working. I noticed in the CloudFormation definition that type
should be TOKEN
, 'COGNITO_USER_POOLS, or 'REQUEST
.
Haven't fully tested yet (waiting on CloudFront distro delete/create), but this has helped understand the non-swagger/AWS:SERVERLESS was of creating an API gateway.
EDIT: code formatting
We are planning to implement this feature on a live Twitch stream next week: https://www.twitch.tv/events/4tjzcuGeSymHA5UCn-MbrA
Would love to have you guys over for real time feedback and inputs!
It would be great if the API would allow modifying the origins (and maybe methods?) to support the following setup:
api.site.com
only allows access forapp.site.com
The OPTIONS request can (should?) return HTTP/1.1 204 No Content
instead of a bogus JSON response.
@spaceemotion not sure I understand the requirement. Wouldn't that just mean specifying Allow-Origin: https://app.site.com
?
Yes. I just thought I'd mention it since you said you'd want to implement the feature next/this week. Not sure what that entails, but setting the origin via props would be nice. Setting it by hand using the methods described above works like a charm.
Cool, yeah. Obvsiouly users should be able to specify the allowed origin(s).
For anybody interested, I managed to setup Gateway response for custom authorizer (when access is denied) with CfnGatewayResponse
:
new CfnGatewayResponse(this, 'AccessDeniedResponse', {
responseType: 'ACCESS_DENIED',
restApiId: api.restApiId, // reference to RestApi created earlier
statusCode: '403',
responseParameters: {
'gatewayresponse.header.x-my-custom-header': "'my-custom-header-value'",
},
responseTemplates: {
'application/json': '{"message":"CUSTOM_ERROR_MSG"}'
}
});
The only problem I have with this solution is that changes to CfnGatewayResponse
are not automatically deployed with cdk deploy
, I have to press manually "Deploy API" in AWS Console.
(1) What is the recommended way of enabling CORS pre-flight for APIG + Lambda proxy integration with the latest CDK? (_I am using 1.33.0
._)
It seems that defaultCorsPreflightOptions
is a prop on ResourceOptions
(a parent of LambdaRestApiProps
), which I think neatly adds everything.
const api = new LambdaRestApi(this, "Api", {
defaultCorsPreflightOptions: {
allowOrigins: ["https://example.com"]
},
// ...other props
});
_I think the answer to (1) is, "Yes, use defaultCorsPreflightOptions
"._
(2) However, does the recommendation change when an authorization type is added? As shown below, I have included IAM authorization which is also applied to the OPTIONS method. Unfortunately, this violates the CORS pre-flight contract or, at least, guarantees it will fail b/c the pre-flight OPTIONS request will not have credentials (per my understanding).
const api = new LambdaRestApi(this, "Api", {
defaultCorsPreflightOptions: {
allowOrigins: ["https://example.com"],
allowCredentials: true
},
defaultMethodOptions: {
authorizationType: AuthorizationType.IAM
},
// ...other props
});
Is there a way to "remove" the authorization type for a specific method (or set to NONE). More generally speaking, can the methods of a REST resource be mutated post-construction?
Alternatively, is there ever a valid case to restrict OPTIONS? (I'm still new to this, but I only see CORS pre-flight mentioned on MDN's OPTIONS doc.) If there are not use cases, can we update the APIG construct to not apply (ignore) the authorization type to OPTIONS methods?
In the mean-time, I'm going to continue use the hand-rolled solution. Thanks for the details on this thread so far everyone.
I'm having the same issue, in SAM this was fixed while ago with a flag AddDefaultAuthorizerToCorsPreflight.
https://github.com/awslabs/serverless-application-model/pull/958
@connorjs What's the hand-rolled solution you're talking about? I tried the @smfontevan solution with no success (I'm still having CORS issues)
@leantorres73 @connorjs can you please raise a new issue so we can track this properly and get it fixed. It's harder for us to track as comments on a closed issue.
@connorjs I have Cognito + API Gateway + Proxy integration + CORS working. It's not a real proxy anymore, but until there is an easy solution to disable security in OPTIONS, this is a way to go
const proxy = api.root.addProxy({
defaultIntegration: new apigateway.LambdaIntegration(handler),
anyMethod: false
});
const authorizerConfig = {
authorizer: {
authorizerId: authorizer.ref
},
authorizationType: apigateway.AuthorizationType.COGNITO
};
proxy.addMethod('GET', undefined, authorizerConfig);
proxy.addMethod('POST', undefined, authorizerConfig);
proxy.addMethod('PUT', undefined, authorizerConfig);
addCorsOptions(proxy);
@connorjs I have Cognito + API Gateway + Proxy integration + CORS working. It's not a real proxy anymore, but until there is an easy solution to disable security in OPTIONS, this is a way to go
const proxy = api.root.addProxy({ defaultIntegration: new apigateway.LambdaIntegration(handler), anyMethod: false }); const authorizerConfig = { authorizer: { authorizerId: authorizer.ref }, authorizationType: apigateway.AuthorizationType.COGNITO }; proxy.addMethod('GET', undefined, authorizerConfig); proxy.addMethod('POST', undefined, authorizerConfig); proxy.addMethod('PUT', undefined, authorizerConfig); addCorsOptions(proxy);
Can you please a separate GitHub issue on this?
Most helpful comment
Just a few edits: