Reputation: 563
I'm trying to enable CORS in my AWS SAM app. Here is the snippet from my template.yaml
:
Globals:
Api:
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: prod
Auth:
Authorizers:
MyCognitoAuthorizer: ...
getByIdFunc:
Type: AWS::Serverless::Function
Properties:
Handler: src/handler.handle
Events:
ApiEvent:
Type: Api
Properties:
Path: /{id}
Method: GET
RestApiId: !Ref MyApi
According to this Using CORS with AWS SAM and that https://github.com/aws/serverless-application-model/issues/373, the cors config should work but unfortunately no header is set on the API response, as seen below.
< HTTP/2 200
< content-type: application/json
< content-length: 770
< date: Tue, 13 Apr 2021 19:55:31 GMT
< x-amzn-requestid: ...
< x-amz-apigw-id: ...
< x-amzn-trace-id: Root=1-...-...;Sampled=0
< x-cache: Miss from cloudfront
< via: 1.1 ...cloudfront.net (CloudFront)
< x-amz-cf-pop: FRA2-C2
< x-amz-cf-id: ...==
<
* Connection #0 to host ....execute-api.eu-central-1.amazonaws.com left intact
[{"model": ..}]
I also tried adding the cors config to the API definition (MyApi) itself like its stated in the offical docs here, but without success.
I could add the header in the response by myself but i rather have it in the template file.
Upvotes: 18
Views: 20565
Reputation: 51
Late to the party, but for anyone else from Google: add this to the Auth section, this won't allow Authorizer to process CORS HTTP headers
Api:
Auth:
AddDefaultAuthorizerToCorsPreflight: false
Upvotes: 5
Reputation: 1033
Using SAM/CloudFormation or AWS Console you can setup CORS headers for OPTIONS method which will be called by browser before calling your actual API method(GET/POST etc). From postman or any other service only your endpoint will be called.
When we are using lambda proxy with API Gateway we need to set the CORS headers in the response object from lambda.
To enable CORS for the Lambda proxy integration, you must add Access-Control-Allow-Origin:
domain-name
to the output headers. domain-name can be*
for any domain name.
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "GET" // Allow only GET request
},
body: JSON.stringify(response)
}
Upvotes: 9
Reputation: 1038
To Enable Cors on API gateway and CloudFormation/SAM work, we need to do a few things:
Here is a sample file that shows how it is being defined:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
SignupApi:
Type: 'AWS::Serverless::Api'
Properties:
StageName: prod
Cors:
AllowMethods: "'POST'"
AllowHeaders: "'Content-Type'"
AllowOrigin: "'*'"
DefinitionBody:
swagger: '2.0'
info:
title: 'Signup API'
version: '1.0.0'
paths:
/signup:
options:
summary: CORS support
description: |
Enable CORS by returning correct headers
consumes:
- application/json
produces:
- application/json
tags:
- CORS
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
{}
responses:
'200':
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
post:
produces:
- application/json
responses:
'200':
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/SignupRequest'
x-amazon-apigateway-integration:
uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SignupFunction.Arn}/invocations'
passthroughBehavior: 'when_no_match'
httpMethod: 'POST'
type: 'aws'
integrationHttpMethod: 'POST'
requestTemplates:
application/json: |
#set($allHeaders = $input.params().header)
{
#foreach($header in $allHeaders.keySet())
"$header": "$util.escapeJavaScript($allHeaders.get($header))" #if($foreach.hasNext),#end
#end
"body": $input.json("$.body")
}
responses:
default:
statusCode: '200'
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: '$input.json("$.body")'
definitions:
SignupRequest:
type: 'object'
properties:
name:
type: 'string'
email:
type: 'string'
password:
type: 'string'
SignupFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.lambda_handler
Runtime: python3.8
InlineCode: |
import json
def lambda_handler(event, context):
response = {
'statusCode': 200,
'body': json.dumps({'message': 'Signup successful'})
}
return response
Events:
SignUp:
Type: Api
Properties:
Path: /signup
Method: post
RestApiId: !Ref SignupApi
Upvotes: 0
Reputation: 1158
What solved it for me was adding the following to my template.yaml:
Globals:
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'content-type'"
AllowOrigin: "'*'"
# AllowCredentials: true Uncomment only if you choose a specific origin instead of the * wildcard.
And just like nirvana124 and Nitesh said, you also need to return these headers with the response in each endpoint:
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "GET" // Allow only GET request
},
body: JSON.stringify(response)
}
Upvotes: 23
Reputation: 101
For anyone using API Gateway Version 2 (HttpApi), it doesn't have support for 'AddDefaultAuthorizerToCorsPreflight', at least not specified in their documentation.
Instead (per AWS guidelines), we need to add a route in lambda function for OPTIONS /{proxy+} and turn off Authentication for this route.
MyHttpApi:
Type: AWS::Serverless::HttpApi
Properties:
Auth:
DefaultAuthorizer: OAuth2
Authorizers:
OAuth2:
JwtConfiguration:
issuer: "..."
audience:
- ...
IdentitySource: "$request.header.Authorization"
CorsConfiguration:
AllowOrigins:
- "*"
AllowMethods:
- GET
- POST
- OPTIONS
AllowHeaders:
- Content-Type
- Accept
- Access-Control-Allow-Headers
- Access-Control-Request-Method
- Access-Control-Request-Headers
- Authorization
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Events:
CorsPreflightEvent:
Type: HttpApi
Properties:
Path: /{proxy+}
Method: OPTIONS
Auth:
Authorizer: NONE
ApiId: !Ref MyHttpApi
Upvotes: 6
Reputation: 840
With your script, CORS is enabled on API Gateway, but SAM always creates PROXY integration from the API Gateway to Lambda, which means that API Gateway cannot add any integration response and it will pass the response which it will receive from LAMBDA. That's why you need to handle the CORS header inside your lambda rather than depending on API Gateway. Below is the sample code to return from the LAMBDA along with the response.
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "OPTIONS,POST,GET,PUT,DELETE,PATCH" // Allow only GET request
},
body: JSON.stringify(response)
}
Upvotes: 3