question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

API Gateway and Lambda TokenAuthorizer in different stacks causing cyclic reference error

See original GitHub issue

❓ General Issue

The Question

Hi,

I have two separate stacks, one for my API Gateway and another for my Lambda that is used as the token authoriser. The Lambda function is then passed to API Gateway via props and is used as the token authoriser. When building the aws-cdk errors mentioning “would create a cyclic reference.” regarding the API Gateway and the token authoriser referencing the Lambda.

I have provided examples and a stack trace under Other information for further details.

Please let me know how this may be resolved (Not sure if this is a bug (seems like a bug) or as designed or user error)?

P.S. My preference is to have my Lambdas in a separate stack (Allows me to deploy/undeploy Lambdas independently from the API Gateway), however if this is not possible at this moment in time, please let me know.

Kind Regards,

RJ

Environment

  • CDK CLI Version: 1.42.0 (build 3b64241)
  • Module Version: all modules versions are 1.42.0 (i.e. apigateway, lambda, cdk, iam)
  • OS: Ubuntu
  • Language: TypeScript

Other information

Below are examples of the stacks:

Stack 1 - API Gateway

  constructor(stack: cdk.Stack, { integrationLambdaFn, tokenAuthoriserLambdaFn}: Props) {

    const api = new apigateway.RestApi(this, 'RestApi', {
      description: 'My API Gateway REST API',
      restApiName: 'MyApiGatewayRestApi',
      policy: policyDocument,
      deployOptions: {
        stageName: 'stage'
      },
      defaultIntegration: new apigateway.LambdaIntegration(integrationLambdaFn),
      defaultMethodOptions: {
        authorizer: new apigateway.TokenAuthorizer(this, 'MyTokenAuthoriser', {
          handler: tokenAuthoriserLambdaFn
        })
      }
    });

Stack 2 - Lambda - TokenAuthoriser

    this.tokenAuthoriserLambdaFn= new lambda.Function(scope, 'Function', {
    functionName: 'myTokenAuthoriser',
    runtime: lambda.Runtime.NODEJS_12_X,
    code: lambda.Code.fromAsset('./lambda'),
    handler: 'index.myTokenAuthoriserHandler',
    role: myRole,
    timeout: cdk.Duration.seconds(15),
    tracing: lambda.Tracing.ACTIVE,
    vpc,
    securityGroups: [securityGroup1] 
  });
  this.tokenAuthoriserLambdaFn.node.addDependency(policy);

As you can see in Stack 1 above, two Lambdas are passed to the API Gateway construct. The first (integrationLambdaFn) is passed to apigateway.LambdaIntegration(integrationLambdaFn) and does not cause the cyclic dependency error. However the second (tokenAuthoriserLambdaFn) passed to the apigateway.TokenAuthorizer via the apigateway.TokenAuthorizerProps props causes the cyclic dependency error.

Below is an example stack trace:

Error: 'lambda' depends on 'apigateway' (lambda -> apigateway/MyApiGateWay/RestApi/Resource.Ref, lambda -> apigateway/MyApiGateWay/TokenAuthoriser/Resource.Ref). Adding this dependency (apigateway -> lambda/MyTokenAuthoriser/Function/Resource.Arn) would create a cyclic reference.
 
    at ApiGatewayStack._addAssemblyDependency (/home/builds/project/node_modules/@aws-cdk/core/lib/stack.js:409:19)
 
    at Object.addDependency (/home/builds/project/node_modules/@aws-cdk/core/lib/deps.js:39:24)
 
    at ApiGatewayStack.addDependency (/home/builds/project/node_modules/@aws-cdk/core/lib/stack.js:188:16)
 
    at resolveValue (/home/builds/project/node_modules/@aws-cdk/core/lib/private/refs.js:84:14)
 
    at Object.resolveReferences (/home/builds/project/node_modules/@aws-cdk/core/lib/private/refs.js:26:30)
 
    at Object.prepareApp (/home/builds/project/node_modules/@aws-cdk/core/lib/private/prepare-app.js:35:16)
 
    at App.prepare (/home/builds/project/node_modules/@aws-cdk/core/lib/app.js:81:23)
 
    at App.onPrepare (/home/builds/project/node_modules/@aws-cdk/core/lib/construct-compat.js:71:14)
 
    at Node.prepare (/home/builds/project/node_modules/constructs/lib/construct.js:365:20)
 
    at Node.synthesize (/home/builds/project/node_modules/constructs/lib/construct.js:323:14)
 

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:6
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

3reactions
rjoseph-resilientcommented, Jun 15, 2020

Hi,

For anyone else who may run into this…

I have worked around this by importing the Lambda via its ARN instead and setting up a custom Role.

Example:

import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigateway from '@aws-cdk/aws-apigateway';

const tokenAuthLambdaFn = lambda.Function.fromFunctionArn(
      this,
      'tokenAuthoriser',
      `arn:aws:lambda:${region}:${account}:function:my-lambda-tokenAuthoriser`
    );

    // Role and policies to allow TokenAuthoriser Lambda to be invoked by API Gateway
    const invokeTokenAuthoriserRole = new iam.Role(this, 'Role', {
      roleName: 'my-api-gateway-role`,
      assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
    });
    const invokeTokenAuthoriserPolicyStatement = new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      sid: 'AllowInvokeLambda',
      resources: ['*'], // or the ARN of your TokenAuthoriser  Lambda
      actions: ['lambda:InvokeFunction']
    });
    const policy = new iam.Policy(this, 'Policy', {
      policyName: 'my-api-gateway-policy',
      roles: [invokeTokenAuthoriserRole],
      statements: [invokeTokenAuthoriserPolicyStatement ]
    });

    const authorizer = new apigateway.TokenAuthorizer(this, 'TokenAuthoriser', {
      handler: tokenAuthLambdaFn,
      assumeRole: invokeTokenAuthoriserRole
    });

Finally I apply the authorizer to the required method:

rest.addResource('endpoint').addMethod('POST', lambdaIntegration, {
        requestModels: { 'application/json': new apigateway.Model() },
        requestValidator,
        authorizer // This is the TokenAuthorizer from above example
      });
    }

Hope you find this helpful.

Kind Regards,

Ricardo

0reactions
phamhuycommented, Mar 29, 2022

Facing same problem. Not sure how, but it works when I use HttpLambdaAuthorizer + Http API with the same dependency graph as stated in this issue. But when I switched to TokenAuthorizer + Rest API, I got the issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Handling circular dependency errors in AWS CloudFormation
A common error that you may get when working with AWS CloudFormation is the circular dependency error: error message.
Read more >
@aws-cdk/aws-apigateway - npm
Start using @aws-cdk/aws-apigateway in your project by running `npm i ... IAM-based authorizer; Lambda-based token authorizer; Lambda-based request ...
Read more >
AWS CDK: What is the best way to implement multiple Stacks?
Stack for REST API definitions using API Gateway · Stack for Lambda functions running business-specific operations (Ex: CRUDs) · Stack for Lambda ......
Read more >
awslabs/aws-cdk - Gitter
The Lambda is called okay from another API gateway (the manually created one) so ... eventhough adding stack dependecy getting error as cyclic...
Read more >
The Complete Guide to Custom Authorizers with AWS ...
API Gateway custom authorizers are a great way to separate auth logic ... The event object in your Lambda function for a token...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found