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)
Top comments (4)
As of v1.88 this is now supported directly in CDK, see this commit for an example: github.com/aws/aws-cdk/pull/12786/...
TL;DR (from GitHub)
Any idea how to add an Authorizer to a "proxied" LambdaRestApi object? One that refers all the calling paths to the lambda and allows the lambda to handle the "routing". (I'm using Nestjs in the lambda.).
I tried:
this._authLambda = new Function(this,
${ options.stackName }-auth-lambda
,{
runtime: Runtime.NODEJS_12_X,
code: Code.fromAsset("./lambdas/auth"),
handler: "main.handler",
logRetention: 7,
timeout: Duration.seconds(20),
layers: [ options.nestJsLayer ]
}
);
this._gatewayRestAPI = new LambdaRestApi(this,
${ options.stackName }-auth-api
, {handler: this._authLambda,
});
const authIntegration = new LambdaIntegration(this._authLambda);
const path = this._gatewayRestAPI.root.addResource('*');
path.addMethod("POST", authIntegration,{
authorizer: options.customAuthorizer
});
But the cdk says: "Cannot call 'addResource' on a proxying LambdaRestApi; set 'proxy' to false". I guess I could go through and add the routes of my nestjs app to the rest api, but I hate having two places to maintain them.
Hmm, been a while since I've dealt with the CDK, but I'd think there'd be another method on the REST API to do that. Alternatively, I'd suggest using the CDK to generate the cloudformation template without the offending piece, compare that to a cloudformation template doing what you want to do and use add_property_override to correct the CDK version
Link to an AWS example addressing the issue: github.com/aws-samples/aws-cdk-exa...