-
-If your browser's CORS preflight request is responding with a 403, your API's OPTIONS method could be requiring authentication. This might sound okay if your API is authenticated, but the OPTIONS method behaves differently than other HTTP methods. Your API server will need to comply with the CORS standard, which requires OPTIONS methods to work without authentication headers.
For non-simple HTTP requests, your browser sends a preflight request to check if the server complies with the browser's CORS configuration. The browser completes this preflight with an OPTIONS request to the endpoint with these three headers: Access-Control-Request-Method
, Access-Control-Request-Headers
, and Origin
. The browser does this itself automatically; developers can't manually send the OPTIONS request on the behalf of the browser.
Therefore, it's not possible to add extra headers like x-api-key
. If your API is designed to require an API key for OPTIONS methods, it's guarenteed to respond with a 403, even if the actual HTTP request includes the API key in a x-api-key
header.
Remove any authenticaion requirements found in your OPTIONS method(s).
If you're using the AWS Serverless Application Model (SAM), you know how awesome it can be for creating API's. It can automatically handle CORS and authentication all with just a couple lines of a SAM template.
However, make sure to NOT declare ApiKeyRequired: true
inside the Auth
property of AWS::Serverless::API
. This will require an API key for all methods, including the OPTIONS methods SAM automatically creates to handle CORS.
SampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Auth:
ApiKeyRequired: true # <-- BAD; CAUSES 403's ON OPTIONS
UsagePlan:
CreateUsagePlan: PER_API
Description: Sample usage plan
Throttle:
BurstLimit: 10000
RateLimit: 5000
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: !Sub
- "'https://www.${Domain}'"
- Domain: !Ref DomainName
Domain:
DomainName: !Join ['.', [api, !Ref DomainName]]
CertificateArn: !Ref AcmCertificateArn
EndpointConfiguration: EDGE
Route53:
HostedZoneId: !Ref HostedZone
If you want to build an API with SAM that requires an API key for every method, manually define the API key requirement in every AWS::Serverless::Function
definition under it's Events
property.
SampleFunction:
Type: AWS::Serverless::Function
Properties:
Handler: handlers/index.sampleFunction
Description: This is a sample
Policies:
- AWSLambdaBasicExecutionRole
Events:
Api:
Type: Api
Properties:
RestApiId: !Ref SampleAPI
Path: /endpoint
Method: GET
Auth:
ApiKeyRequired: true # <-- HERE
Currently, there is no way with SAM to globally require API keys and still satisfy CORS. You also can't standardize API key requirements in the Globals
template section because the Events
property needs to be unique to every function. Although this might seem like a hassle, authenticating an API with only API keys is not advised. AWS offers plenty other authentication solutions that are more robust. If you're experiencing this problem and only authenticating your API with API keys, it might be time investigate in a beefier authorization solution, such as IAM's or a Lambda authorizer.