Alright, I'm ready to look very silly. I'd bet money this is a simple problem, I'm just not seeing what I am doing wrong.
I recently deployed a SAM application that sets up an API Gateway. Relevant configuration as followed
# SAM Template Headers + Some Parameters; nothing relating to this.
Resources:
# The Api Gateway necessary for the authorizers.
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Cors: "'*'"
Auth:
DefaultAuthorizer: MyCognitoAuthorizer
Authorizers:
MyCognitoAuthorizer:
UserPoolArn: !ImportValue UserPoolArn
# some other functions, but one function should do it.
# Verified that all functions are referencing the custom ApiGateway resource.
PutTodosFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/add.putItemHandler
Description: Adds a todo.
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TodosTable
Environment:
Variables:
TODOS_TABLE: !Ref TodosTable
NODE_ENV: !If [isProd, 'production', 'development']
REGION: !Ref AWS::Region
Events:
PutTodo:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /
Method: PUT
And here's where I feel like I'm going crazy. As you can see, my API Gateway uses AWS Cognito for authorization, so I was testing the flow using the Insomnia desktop application.
All requests work fine, so long as I remember to supply the necessary token. I figured I was golden until I started trying to make the same requests through the browser.
Now I get errors like the following when trying to make the request:
Access to fetch at 'https://xpquux202j.execute-api.us-west-2.amazonaws.com/alpha/' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The code calling this has gone through some iterations, but the most recent is:
const headers = new Headers();
headers.append('Authorization', 'Bearer <JWT Token Goes Here>');
fetch(API_URL, {
headers,
method: 'options', // have tried put/get as well. Neither goes through.
mode: 'cors' // tried cors and no-cors
})
.then(console.log)
.catch(console.log);
Originally with the error I thought something on the API Gateway side was misconfigured, but making an OPTIONS
request to the endpoint (in Insomnia) gives:
TYIA for any help; I'm sure it's something small that I'm missing I'm sort of tired of trying to figure this out.
Alright so the original question is resolved... sort of.
Tl;Dr you can use the AddDefaultAuthorizerToCorsPreflight
option in the Auth
key of the AWS::Serverless::Api
call, e.g.
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Cors: "'*'"
Auth:
DefaultAuthorizer: MyCognitoAuthorizer
AddDefaultAuthorizerToCorsPreflight: false
Authorizers:
MyCognitoAuthorizer:
UserPoolArn: !ImportValue UserPoolArn
however, this creates another CORS related issue. When you create the API Gateway with this setup the OPTIONS request does not have the Access-Control-Allow-Headers
header in its response. I'm still waiting to hear back from the AWS Sam team about it on the Github issue here: https://github.com/aws/serverless-application-model/issues/2136#issuecomment-915708356
So far the work around is to simply enable CORS from the console, which will then have the necessary Authorization headers. Obviously not great, but also not particularly game breaking either.
After some digging, I've found that making these changes gets the API Gateway to deploy with no errors. The CORS section needs to look like this in the ApiGateway resource.
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Cors:
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowMethods: "'*'"
AllowOrigin: "'*'"
# same Auth: definition as above.