Anna Aitchison
Posted on July 16, 2020
I recently spent days trying to figure out how to make Cognito authentication with a REST API work in the AWS CDK, to the point that I even filed a (unnecessary) bug report, so I figured I might as well make that the subject of my first dev.to post as it's pretty short and sweet.
The problem
Adding a authorizer to the API is deceptively easy. You have to use the underlying CloudFormation resource as this feature isn't fully built out in the CDK yet, but the authorizer gets added to the API in a completely normal manner with the below code.
api = aws_apigateway.RestApi(self, 'API', rest_api_name='API')
auth = aws_apigateway.CfnAuthorizer(self, "adminSectionAuth", rest_api_id=api.rest_api_id,
type='COGNITO_USER_POOLS',
identity_source='method.request.header.Authorization',
provider_arns=[
'arn:aws:cognito-idp:...'],
name="adminSectionAuth"
)
However, adding it to the method is another matter. Passing the object doesn't work - it doesn't error out initially, but the ID of the authorizer doesn't populate in the template so it fails as soon as CDK tries to create the resource.
resource = api.root.add_resource("endpoint")
lambda_function = aws_lambda.Function(self, "lambdaFunction",
handler='app.lambda_handler',
runtime=aws_lambda.Runtime.PYTHON_3_8,
code=aws_lambda.Code.from_asset("path/to/code")
)
lambda_integration = aws_apigateway.LambdaIntegration(lambda_function, proxy=True)
method = resource.add_method("GET", lambda_integration,
authorization_type=AuthorizationType.COGNITO,
authorizer=auth
)
Passing the logical_id or ref properties of the object don't work either - the authorizer parameter needs to be a object.
The Answer
It turns out that you actually have to override properties of the object to get it working, namely the troublesome AuthorizerId field that wasn't populating before.
resource = api.root.add_resource("endpoint")
lambda_function = aws_lambda.Function(self, "lambdaFunction",
handler='app.lambda_handler',
runtime=aws_lambda.Runtime.PYTHON_3_8,
code=aws_lambda.Code.from_asset("path/to/code")
)
lambda_integration = aws_apigateway.LambdaIntegration(lambda_function, proxy=True)
method = resource.add_method("GET", lambda_integration)
method_resource = method.node.find_child('Resource')
method_resource.add_property_override('AuthorizationType', 'COGNITO_USER_POOLS')
method_resource.add_property_override('AuthorizerId', {"Ref": auth.logical_id})
I found this on GitHub in a very informative comment
Comment for #723
I know that this isn't a support forum, but this issue is one of the top results on Google. If it's inappropriate, please move it or let me know where to post.
For anyone who is looking to use the CDK, but got bitten by one of these missing features. There is an official way to work around these issues. Basically we can alter the CloudFormation resources directly. Similar concept to boto3 clients.
Here is an example of how to add an Authorizer in Python.
Assume we have an API Gateway and a POST a method:
api_gw = aws_apigateway.RestApi(self, 'MyApp')
post_method = api_gw.root.add_method(http_method='POST')
Set the authorizer using a low level CfnResource:
api_gw_authorizer = aws_apigateway.CfnAuthorizer(
scope=self,
id='my_authorizer',
rest_api_id=api_gw.rest_api_id,
name='MyAuth',
type='COGNITO_USER_POOLS',
identity_source='method.request.header.name.Authorization',
provider_arns=[
'arn:aws:cognito-idp:eu-west-1:123456789012:userpool/'
'eu-west-1_MyCognito'])
Get the underlying CfnResource for the POST method created above:
post_method_resource = post_method.node.find_child('Resource')
Set the POST method to use the authorizer by adding the required CloudFormation properties to the low level resource:
post_method_resource.add_property_override('AuthorizationType',
'COGNITO_USER_POOLS')
post_method_resource.add_property_override(
'AuthorizerId',
{"Ref": api_gw_authorizer.logical_id})
Take note of the second instruction, that's a dictionary. It needs to be, so that the AuthorizedId property is added correctly, like:
AuthorizerId:
Ref: myauthorizer
instead of something like:
AuthorizerId: "Ref: myauthorizer"
As of 0.35.0, the above should output a template containing:
MyAppPOST853D1BB4:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
ResourceId:
Fn::GetAtt:
- MyApp3CE31C26
- RootResourceId
RestApiId:
Ref: MyApp3CE31C26
AuthorizationType: COGNITO_USER_POOLS
AuthorizerId:
Ref: myauthorizer
Integration:
Type: MOCK
myauthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
RestApiId:
Ref: MyApp3CE31C26
Type: COGNITO_USER_POOLS
IdentitySource: method.request.header.name.Authorization
Name: MyAuth
ProviderARNs:
- arn:aws:cognito-idp:eu-west-1:123456789012:userpool/eu-west-1_MyCognito
(removed Metadata for brevity)
Posted on July 16, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
August 20, 2024