I have built an SPA which has react as its front-end and the serverless(AWS lambda) framework has its back-end.
The react app has been deployed in S3. This app communicates to the lambda functions(serverless) through AWS API Gateway.
Since the react app which is in S3 domain and the back-end (AWS API Gateway, AWS lambda, DynamoDB) is in different domain. CORS problem occurs.
To rectify the CORS problem, I have hardcoded the Access-Control-Allow-Origin header as react app's domain URL. So, the backend understands the origin is from known source and resolves the origin-CORS error.
But, There is another header called "Access-Control-Allow-Credentials" which has to be true to access request's cookies.
IF Access-Control-Allow-Origin is "*", then Access-Control-Allow-Credentials must be false, for privacy concern.
so, I have set Access-Control-Allow-Origin to React's domain, then Access-Control-Allow-Credentials may be true.
I added Access-Control-Allow-Credentials header in API-gateway response method and in integration response method. The problem is I am not getting the Access-Control-Allow-Credentials header in client side as response. I am getting all other headers except this one. Testing API Gateway console gives the header, but testing on postman and in browser, does not receive the above header.
The headers in client are
but on testing AWS API Gateway gives this .
The problem is Access-Control-Allow-Credentials header is not retrieved as response in browser and in postman, but appears when testing in AWS API Gateway console. What might be the problem ?
Please post exported Swagger for your API. I can confirm there is nothing special about "Access-Control-Allow-Credentials" header, so you should be able to map this according to your needs.
Example API:
swagger: "2.0"
info:
version: "2016-09-17T00:36:34Z"
title: "foo"
host: "45c24yfor1.execute-api.us-east-1.amazonaws.com"
basePath: "/test"
schemes:
- "https"
paths:
/:
get:
consumes:
- "application/json"
produces:
- "application/json"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Cookie:
type: "string"
Access-Control-Allow-Credentials:
type: "string"
Set-Cookie:
type: "string"
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Credentials: "'test'"
requestTemplates:
application/json: "{\"statusCode\": 200}"
passthroughBehavior: "when_no_match"
type: "mock"
definitions:
Empty:
type: "object"
title: "Empty Schema"
Command:
curl -v https://45c24yfor1.execute-api.us-east-1.amazonaws.com/test
< HTTP/1.1 200 OK
< Date: Sat, 17 Sep 2016 00:36:46 GMT
< Content-Type: application/json
< Content-Length: 0
< Connection: keep-alive
< Access-Control-Allow-Credentials: test
< x-amzn-RequestId: d04135bc-7c6e-11e6-a593-559956e50e8a
< X-Cache: Miss from cloudfront
< Via: 1.1 b63769e2d89c89274acd908e4bfcb9f4.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: 3Nevesi15lEGox0jrFq8B2HEknHbbFfISlg4yv7Lw3X90S2sUIWE_g==
Related
All I want is deploying a github page which launches a cross site post request with payload {"statement":"select * from t"} to aws lambda API url like https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/default/foo. So I create a aws lambda, write some code and create a API Gateway trigger.
When I test my page I found the OPTIONS request is successful but the header is like
apigw-requestid: WjJcajPTtjMEPTw=
content-length: 0
date: Mon, 08 Aug 2022 14:50:30 GMT
xxxx: aaaa
As you can see, the xxxx header is OK but the Access-Control-Allow-Origin header not. This caused the POST request failed.
the aws lambda code:
exports.handler = async (event,context, callback) => {
// I add this because the browser make a OPTIONS request first before axios.post()
if (event.httpMethod == 'OPTIONS') {
return {
headers: {
"xxxx":"aaaa",
"Access-Control-Allow-Origin": "*",
},
statusCode: 200,
}
}
// handle POST case
}
the API Gateway trigger setting:
API type: HTTP
Authorization: NONE
CORS: Yes
Detailed metrics enabled: Yes
Method: ANY
Resource path: /foo
Service principal: apigateway.amazonaws.com
Stage: default
Statement ID: xxxxxxxxxxx
I tried to add CORS to my HTTP API and it does work for GET, POST, etc. but not for OPTIONS calls. What could be the reason?
It is a completely new HTTP API in AWS API Gateway. I added some hello world lambda function as a route and * as an allowed origin. I thought the whole point of OPTIONS calls is to send these headers...
Here is Postman requesting POST
And here with OPTIONS
This is my routes config
And this is my CORS config
You should be able to easily add OPTIONS support for your API in your specification:
options:
summary: CORS support
description: |
Enable CORS by returning correct headers
tags:
- CORS
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Origin:
schema:
type: string
Access-Control-Allow-Methods:
schema:
type: string
Access-Control-Allow-Headers:
schema:
type: string
content: {}
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: |
{}
This should work for non proxy integrations and returns the correct headers directly from the API and not from the lambda. You may need to modify the response parameters to suit your needs.
For proxy integrations, you need to implement the response in the lambda function (https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html)
Hello, I'm trying to solve my issue for so long that I had to ask someone else.
I'm working with some API and I want to create resource using POST method via Postman. To create that resource I have to attach kubeconfig file to the POST request. This resource need to have name, description and of course config file. So I filled required fields:
POSTMAN fields
but I receive response "Bad Request":
Invalid Input:: Does not match pattern '^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$'
It's look like i don't understand how to upload files that way because my request body looks like:
Request Body
metadata: "{"name": "edge01", "description": "Edge k8s cluster"}"
file: undefine
In the API documentation we can find that request body should look like this:
Request body multipart/form-data
metadata
object
file
string($binary)
Entire POST entry below:
Network
Request Headers
Accept: */*
Postman-Token: 8857f718-bb05-404c-865a-51ee3c0abe6f
Host: <Hidden_IP>:30461
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------175308278007362265143565
Content-Length: 3518
Request Body
metadata: "{"name": "edge01", "description": "Edge k8s cluster"}"
file: undefined
Response Headers
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Fri, 30 Jul 2021 10:19:58 GMT
Content-Length: 84
Response Body
Invalid Input:: Does not match pattern '^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$'↵
I couldn't find anything about response presented above.
If anyone needs additional information, please comment. This string($binary) is interesting but I don't understand what it means, and couldn't find any informations in POSTMAN context.
This is a piece of API yaml:
/cluster-providers/{cluster-providers-name}/clusters:
parameters:
- $ref: '#/components/parameters/clusterProviderName'
post:
tags:
- Clusters
summary: Add Cluster
description: Add a new `cluster`
operationId: addClusterToClusterProvider
responses:
'201':
description: Created
content:
application/json: # operation response mime type
schema:
$ref: '#/components/schemas/Metadata'
'400':
description: Bad Request
'404':
description: Not Found
'409':
description: Conflict
'422':
description: Unprocessable Entity
'500':
description: Internal Server Error
requestBody:
content:
multipart/form-data: # Media type
schema: # Request payload
$ref: '#/components/schemas/AppData'
Add metadata to the body under raw, instead of form-data. This should be an object, like this:
{
"metadata": {
"name": "edge01",
"description": "Edge k8s cluster"
}
}
I have an OpenAPI spec for an api which I am deploying via CDK. The spec looks like:
openapi: 3.0.1
info:
title: My API
description: My REST API with CORS enabled
version: 0.1.0
x-amazon-apigateway-cors:
allowOrigins:
- "*"
allowCredentials: true
exposeHeaders:
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
maxAge: 3600
allowMethods:
- "*"
allowHeaders":
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
- "Authorization"
components:
securitySchemes:
lambda:
type: "apiKey"
name: "Authorization"
in: "header"
x-amazon-apigateway-authtype: "custom"
x-amazon-apigateway-authorizer:
authorizerUri: "{{my-lambda-authorizer}}"
authorizerResultTtlInSeconds: 300
type: "token"
paths:
/user/{id}:
get:
summary: Get info of specified user.
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
security:
- lambda: []
x-amazon-apigateway-integration:
uri: "{{my-lambda}}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
When I try to access this via fetch(), I get an error Failed to load resource: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.
fetch('https://api.example.com/user/1')
.then(response => response.json())
.then((user: User) => {
// do something
})
.catch((err) => {
console.log("Error: " + err);
});
The API is accessible at api.example.com and I am running the website locally using Gatsby at localhost:8000.
The AWS docs seem to state that CORS is enabled when I put x-amazon-apigateway-cors at the root of the spec, but CORS doesn't seem to be enabled (or working) when I try to access the API. How do I enable CORS for my API without needing to configure it in the console?
Amazon API Gateway offers two types of APIs: REST APIs and HTTP APIs. REST APIs were the kind of APIs originally introduced with Amazon API Gateway, while HTTP APIs got announced at the end of 2019.
Based on the description of your OpenAPI specification I assume you're trying to deploy a REST API. The x-amazon-apigateway-cors OpenAPI extension however only works for HTTP APIs.
You have two choices now: You either switch to use a HTTP API or you configure CORS manually. As long as you don't need features only supported by REST APIs, I suggest you switch to use a HTTP API, as that's the more modern kind of API Amazon API Gateway offers.
If you want to use a REST API, enabling CORS requires more manual configuration. That's documented by AWS in Enabling CORS for a REST API resource. Essentially you have to ensure your integration returns proper CORS headers. For a NodeJS AWS Lambda function that could look like:
exports.handler = async (event) => {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "https://www.example.com",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
For CORS pre-flight requests to work you'd also have to ensure that OPTIONS-requests also return the correct headers. The easiest way to achieve that is to add a mock-integration for the OPTIONS-request to your OpenAPI specification. That would look like:
options:
responses:
'200':
description: Default response
headers:
Access-Control-Allow-Headers:
schema:
type: string
Access-Control-Allow-Methods:
schema:
type: string
Access-Control-Allow-Origin:
schema:
type: string
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{"statusCode" : 200}
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'*'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Origin: "'https://example.com/'"
Please also mind that modern browsers don't support localhost as origin for CORS, so you might need to work around that as well.
In order to prevent users who have not logged in to call my lambda function through the AWS API Gateway, I'm using the Custom Authorizer lambda solution.
If the request is authorized (200) and I get a response from the called lambda everything works fine and I get the Access-Control-Allow-Origin header.
But if the request is not authorized, I get a 401 that has no Access-Control-Allow-Origin header, therefore preventing me from reading the 401 status of the response and redirecting the user to the log-in page.
I believe this is because the Custom Autorization mechanism is unaware that the request needs to use CORS. Does anyone know that this is actually the issue? Are you aware of any possible solution?
I'm happy to announce the new Gateway Responses feature which allows you to customize the error responses for requests that don't call your integration. This allows you to ensure that CORS headers are included, even on failed auth requests.
Read more in our documentation, which includes a CORS example.
Yes, this is a known bug with API Gateway custom authorizers. Thanks for bringing this to our attention. The team will update this post when we've deployed a fix. Apologies for the inconvenience.
The easiest way to resolve this for all 4XX error (including 401 errors) is to go to "Gateway Responses" and then select "Default 4XX" and then add the the header "Access-Control-Allow-Origin" with the value '*'.
See screenshot:
This works for me (inline in AWS::APIGateway: definition)
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: Dev
GatewayResponses:
UNAUTHORIZED:
StatusCode: 401
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
ACCESS_DENIED:
StatusCode: 403
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
StatusCode: 500
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
RESOURCE_NOT_FOUND:
StatusCode: 404
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
The available GatewayResponses Naming are:
DEFAULT_INTERNAL
DEFAULT_4XX
DEFAULT_5XX
RESOURCE_NOT_FOUND
UNAUTHORIZED
ACCESS_DENIED
AUTHORIZER_FAILURE
AUTHORIZER_CONFIGURATION_ERROR
MISSING_AUTHENTICATION_TOKEN
INVALID_SIGNATURE
EXPIRED_TOKEN
INTEGRATION_FAILURE
INTEGRATION_TIMEOUT
API_CONFIGURATION_ERROR
UNSUPPORTED_MEDIA_TYPE
REQUEST_TOO_LARGE
BAD_REQUEST_PARAMETERS
BAD_REQUEST_BODY
THROTTLED
QUOTA_EXCEEDED
INVALID_API_KEY
WAF_FILTERED
So you could specify the Response customization for these Controlled AWS responses.
Because it took me a while to figure out how to put it all together in Cloud Formation, here is a snippet showing how to set it up.
...
MyApi:
Type: "AWS::ApiGateway::MyApi"
Properties:
Description: My API
Name: "my-api"
MyApiAuthorizer:
Type: "AWS::ApiGateway::Authorizer"
Properties:
Name: "my-api-authorizer"
IdentitySource: "method.request.header.Authorization"
ProviderARNs:
- !GetAtt MyUserPool.Arn
RestApiId: !Ref MyAApi
Type: COGNITO_USER_POOLS
MyApiGatewayResponse:
Type: "AWS::ApiGateway::GatewayResponse"
Properties:
ResponseParameters:
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
ResponseType: UNAUTHORIZED
RestApiId: !Ref MyApi
StatusCode: "401"
If, like me, you are running into problems with API Gateway V2, specifically with an HTTP API - the ANY method doesn't seem to work with the plug and play CORS offering. I had to individually create a route for each method (annoying because they all call the same lambda function, but oh well).
Adding to the answers above, if you're not using Cloudformation/SAM template, you can save yourself some manual steps using this python script:
import boto3
import sys
if len(sys.argv) != 3:
print("usage: python script.py <API_ID> <STAGE>")
exit()
client = boto3.client('apigateway')
response = client.put_gateway_response(
restApiId=sys.argv[1],
responseType='UNAUTHORIZED',
statusCode='401',
responseParameters={
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
}
)
response = client.create_deployment(
restApiId=sys.argv[1],
stageName=sys.argv[2])
To fix this for our SPA (we are using AWS Cognito authorizer) we added the next Response Headers in DEFAULT 4xxx and DEFAULT 5xxx Gateway responses:
Access-Control-Allow-Origin '{url_of_your_front-end}'
Access-Control-Allow-Headers '{url_of_your_front-end}'
we set {url_of_your_front-end} instead of '*', because the browser didn't like it :D
as an additional we set Access-Control-Allow-Credentials 'true' header to make a browser happy.
Picture with all headers
Adding the response header "Access-Control-Allow-Origin" for the Unauthorized response should fix this issue
Select the Api
Select the Gateway Responses
Select Unauthorized
Edit the response header and add Access-Control-Allow-Origin with value "*"