-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot set DefaultAuthorizer and have CORS enabled #717
Comments
@cidthecoatrack Thanks for posting this. Just spend a day trying to figure out if I did something wrong. Having the options require authorization does not make sense. |
@cidthecoatrack I have been dealing with the same problem, but I found a fifth option: Use a macro to remove the authorizer from options paths. It took a bit of stumbling, but I'll share: For your existing template, we need to add another transformation. I'm going to call the transformation CorsFixer. To apply this transformation change the line:
to:
Transforms are run in order, so it will run after the Serverless transform has applied the Authorizers and Cors. Sadly, macros have to be defined in a separate stack from where they are used. Here's how I accomplished that:
The javascript here is pretty simple. It finds all of the RestApi resources, then searches for all options requests and overwrites any authorization w/ "NONE". I'm not a cloudformation expert, but this should work in the majority of cases. Package and deploy that to a dedicated stack, then package and deploy your normal stack and you should be good to go! One note: when I first set this up, ApiGateway got the changes (authorization disabled) but it doesn't seem like it deployed. I manually deployed and everything worked great. Since then, I've done multiple deployments via SAM and everything has worked great. Hope this helps! |
I was able to get a 6th workaround, though it is not as great as the macro option and I am going to try that next. Also, we don't use swagger in our serverless templates, that may make a difference, given #650. But the basic idea is that you specify 2 events in the function for each path you want to authorize: one event uses
This is working for me when I have a |
@danludwig, your workaround actually calls related lambda function with OPTIONS method, so you'll have to handle preflight request into your code. So, I suggest @disciplezero workaround which doesn't call any code, I use it and worked like a charm. |
@guijob yes agreed! We still need the workaround when debugging with auth enabled on the endpoint locally, but we strip out the options events before committing so that code never gets hit after deployed with the macro transform. |
I'm seeing the same the same issue. The only workaround I have found that doesn't require macros or other additional implementation is to not set a DefaultAuthorizer but to apply they Authorizer on each resource instead, when doing it this way OPTIONS will not be authorized but instead falls back to NONE as expected. OPTIONS requests should not be authorized at all, regardless of what the DefaultAuthorizer is set to. Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
This also relates to #815 since GatewayResponses are required to fully handle CORS on requests where the Authorizer denies the request. |
Python isn't a language I've used much but I am working on fixing this and adding unit tests to detect regressions. It looks like |
Sorry for being late to the party. There are valid use cases for adding Auth to preflight. I have provided @jbutz with the following comment on their PR #828, and then once everything is ready we can get that merged in. | I don't think we should be adding this in every scenario. Please add an additional property to the |
It would be really nice if there is a simple flag to prevent the authorizer to be set on the OPTIONS method, without workarounds and stuff. The OPTIONS preflight call is usually made automatically by the browser, not by the application code. If you use an HTTPInterceptor to add Authorization headers to each outgoing request of the webapp, the OPTIONS preflight requests are not intercepted and the API cannot be used because the preflight fails due to missing authorization. Thanks |
I agree with @richiewebgate |
We are happy to work with anyone interested in submitting a PR for this option. |
I was struggling quite a lot today with this and I've ended up to remove completely the
|
Nice alternative @stavros-zavrakas, I'll try that solution next week. |
This should be a pretty simple addition if someone wants to work on this. #828 has the bulk of the work; I'm asking for an additional property under API |
I can't make the Macro work @disciplezero FAILED - Failed to execute transform ACCOUNT-NUMBER::CorsFixer Any suggestions? |
@leantorres73 - Not an expert in CloudFormation, so the error doesn't give me any insights. We're using this in production, but I basically haven't touched it since we launched that template. My first guess is that you didn't install the CorsFix macro. In your template when you say:
It has to find those transforms. Not sure how it resolves the AWS one, but CorsFixer isn't publicly available. You have to deploy that template in your own account. The template I posted above I have saved as
Then I would try again. If that doesn't help - you might need to look at CloudWatch. The TransformFunction in the macros.yml is just a lambda function. It will have the normal CloudWatch logs and they may surface insights. Ours is named something like In normal operation you should see entries in there for when the lambda starts/stops. No other content. Hope that helps! |
Hi @disciplezero, thanks for the help. I did everything you said but it's still not working for me. The lambda is never invoked. Checking the Cloudformation log it appears that message. This is weird because it shouldn't fail if the CorsFixer was created (and it was, I looked into Cloudformation and the macro deploy was successful). I'll continue digging to find what could be wrong. Thanks! |
Hi, The PR Bugfix: CORS Security #828 seems to be accepted. The "Transform" : "AWS::Serverless-2016-10-31" does not apply with it (obviously) . Where I could found the latest availables Transfom template list to use ? |
Released with v1.13.0 |
in which section of the SAM template should the new option "AddDefaultAuthorizerToCorsPreflight" be placed? I tried several combinations and couldn't get the expected results. ... or is this still not released yet? (I am using codepipeline) thanks |
@richiewebgate Under Api -> Auth. I'm using it also in code pipeline so it should work for you too. |
There is an example in #1079 that shows how to use this feature. |
thanks a lot @leantorres73 and @praneetap. I tried it right now and it finally works!! YAY so cool!! |
I have now been fiddling around with CORS, DefaultAuthorizer and Here's a simplified version of my template: Resources:
myApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Staging
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
Auth:
Authorizers:
aadAuthorizer:
FunctionPayloadType: TOKEN
FunctionArn:
Fn::GetAtt:
- authorizerFunctionV1
- Arn
DefaultAuthorizer: aadAuthorizer
AddDefaultAuthorizerToCorsPreflight: false It correctly adds my DefaultAuthorizer to all endpoints, but it also adds it to my generated OPTIONS endpoints, so I get a 401 when the browser makes preflight requests. If I call an OPTIONS endpoint from Postman with a bearer token, then I get the correct response, but this is obviously not possible from the browser. It seems like a regression to the Any thoughts? 😅 |
@DK8ALREC
This person seems to be having a similar issue but using UserPool here. |
A workaround is only define the Authorizer, (disable global authorization (preflight too)), and on every Lambda define the authorizer explicity: This is my working example (hope helps): Resources:
MyAPI:
Type: AWS::Serverless::Api
Properties:
StageName: Dev
GatewayResponses: #https://stackoverflow.com/questions/36913196/401-return-from-an-api-gateway-custom-authorizer-is-missing-access-control-allo/58059560#58059560
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: "'*'"
Cors:
AllowMethods: "'OPTIONS,GET,POST,PUT,DELETE'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key, Access-Control-Allow-Origin'"
AllowOrigin: "'*'"
#AllowCredentials: true
Auth:
# AddDefaultAuthorizerToCorsPreflight: False
# DefaultAuthorizer: CustomLambdaTokenAuthorizer
Authorizers:
CustomLambdaTokenAuthorizer:
FunctionArn: !GetAtt ValidateTokenFunction.Arn
Identity:
Header: Authorization
#ValidationExpression: Bearer.*
GetUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./app
Handler: usersFacade.get
Runtime: nodejs10.x
Policies: AmazonDynamoDBFullAccess
Environment:
Variables:
USERS_TABLE_NAME: !Ref UsersTable
Events:
GetUserPath:
Type: Api
Properties:
RestApiId: !Ref MyAPI
Path: /{apiVersionId}/users/{resourceId}
Method: get
Auth:
Authorizer: CustomLambdaTokenAuthorizer
UpdateUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./app
Handler: usersFacade.updateUserInfo
Runtime: nodejs10.x
Policies: AmazonDynamoDBFullAccess
Environment:
Variables:
USERS_TABLE_NAME: !Ref UsersTable
Events:
PutRoot:
Type: Api
Properties:
RestApiId: !Ref MyAPI
Path: /{apiVersionId}/users/{resourceId}/userInfo
Method: put
Auth:
Authorizer: CustomLambdaTokenAuthorizer
ValidateTokenFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./app
Handler: AuthorizerFacade.validateToken
Runtime: nodejs10.x
Policies:
- AmazonDynamoDBFullAccess
- SecretsManagerReadWrite
Environment:
Variables:
ACCESS_TOKEN_TTL: !Ref AccessTokenTTL
REFRESH_TOKEN_TTL: !Ref RefreshTokenTTL
SECRET: !Ref AuthSecret
TOKEN_TABLE_NAME: !Ref AuthorizationTokenTable |
@ConnorRobertson thanks for the reply. The only differences I can see between what I have tried and this is example is that @danipenaperez thanks, I already changed my API to use this solution. I just don't like the fact that auth has to be specified for each lambda, but that does seem to be the only way. |
Description:
I want to deploy an API Gateway that both has a custom lambda authorizer and uses CORS. However, the configuration always ends up in a non-working state. Because of #650 , the only authorizer you can specify is the DefaultAuthorizer (if you are referencing a swagger at all). This means the authorizer is applied to every single endpoint in the API - including OPTIONS endpoints generated by setting CORS. However, CORS methods are not meant to be authorized - particularly if you are using Headers authorization, since the
Authorization
header is stripped out from CORS pre-flight checks. Which leaves us with the following options:I understand why IdentitySource is required in the serverless template and in aws cli (which begs the question why it can be removed in AWS Console at all), but because #650 is not fixed, we cannot manually associate the authorizer with our explicit endpoints - thereby leaving the generated CORS endpoints unauthorized. Therefore, one cannot have custom authorizers and CORS enabled on an API Gateway
Steps to reproduce the issue:
IdentitySources
), sets that authorizer as theDefaultAuthorizer
, and uses an inlineDefinitionBody
Example template:
(We are using dotnet, but underneath it is basic AWS CLI)
Observed result:
The authorizer has been associated with the
OPTIONS
endpoints, which will cause them to fail.Expected result:
I should be able to either:
The text was updated successfully, but these errors were encountered: