serverless: Cannot reference an authorizer already created within services that shares the same API GW

This is a (Bug Report)

Description

  • What went wrong? I have two services (e.g. Service-A and Service-B) that shares the same API Gateway. Service-A has some public/private endpoints and defines an API GW authorizer. This works fine. Service-B has some private endpoints that need to use the authorizer defined in Service-A. I tried to reference the authorizer by ARN with no success. I think the problem is that serverless tries to create another authorizer in the same API GW, and throws error because an authorizer with the same name already exists. The thing is that I dont want to create another API GW authorizer, I just want to reference an authorizer that belongs to the API GW. Output relevant to Service-A
{
    "AuthorizerApiGatewayAuthorizer":{
        "Type":"AWS::ApiGateway::Authorizer",
        "Properties":{
            "AuthorizerResultTtlInSeconds":0,
            "IdentitySource":"method.request.header.Authorization",
            "Name":"authorizer",
            "RestApiId":"XXXXXXXX",
            "AuthorizerUri":{
                "Fn::Join":[
                    "",
                    [
                        "arn:aws:apigateway:",
                        {
                            "Ref":"AWS::Region"
                        },
                        ":lambda:path/2015-03-31/functions/",
                        {
                            "Fn::GetAtt":[
                                "AuthorizerLambdaFunction",
                                "Arn"
                            ]
                        },
                        "/invocations"
                    ]
                ]
            },
            "Type":"TOKEN"
        }
    }
}

Output relevant to Service-B

{
    "AuthorizerApiGatewayAuthorizer":{
        "Type":"AWS::ApiGateway::Authorizer",
        "Properties":{
            "IdentitySource":"method.request.header.Authorization",
            "Name":"authorizer",
            "RestApiId":"XXXXXXXX",
            "AuthorizerUri":{
                "Fn::Join":[
                    "",
                    [
                        "arn:aws:apigateway:",
                        {
                            "Ref":"AWS::Region"
                        },
                        ":lambda:path/2015-03-31/functions/",
                        "MY_LAMBDA_FUNCTION_AUTHORIZER_ARN",
                        "/invocations"
                    ]
                ]
            },
            "Type":"TOKEN"
        }
    }
}
  • What did you expect should have happened? Having Service-A and Service-B within the same API GW, I expect to be able to reference the authorizer already defined by Service-A.
  • What was the config you used? I have tried this alternatives in Service-B
functions:
 some-function:
   handler: someHandler.someFunction
   events:
     - http:
         path: some/path
         method: get
         authorizer:
           arn: LAMBDA_ARN
           name: service-a-authorizer-name
functions:
 some-function:
   handler: someHandler.someFunction
   events:
     - http:
         path: some/path
         method: get
         authorizer: LAMBDA_ARN
  • What stacktrace or error message from your provider did you see? An error occurred: AuthorizerApiGatewayAuthorizer - Authorizer name must be unique. Authorizer authorizer already exists in this RestApi…

Additional Data

  • Serverless Framework Version you’re using: 1.26
  • Operating System: Linux (kernel 4.13.0-32-generic)
  • Stack Trace:
  • Provider Error messages:

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 19
  • Comments: 28 (6 by maintainers)

Commits related to this issue

Most upvoted comments

For what it’s worth, commenting what I’ve done. I already have an authorizer function deployed in a different region which I would just like to add to a new serverless project:

resources:
  Resources:
    apiGatewayAuthorizer:
      Type: "AWS::ApiGateway::Authorizer"
      Properties:
        Name: "authorizer"
        AuthorizerUri: "arn:aws:apigateway:[region]:lambda:path/2015-03-31/functions/[authorizer-lambda-function-arn]/invocations"
        RestApiId: !Ref "ApiGatewayRestApi"
        Type: "TOKEN"
        IdentitySource: "method.request.header.Authorization"
      DependsOn:
        - "ApiGatewayRestApi"


functions:
  function:
    handler: handler.function
    events:
      - http:
          path: function
          method: post
          authorizer:
            type: "CUSTOM"
            authorizerId:
              Ref: "apiGatewayAuthorizer"

Tested and this works

I hit the same problem - first thought was to use restApiResources but I couldn’t (quickly) work out how to with an authorizer however if you give the authorizer in service B a name as well as an arn then it will at least deploy. (For some reason when I do this it’s not calling the authFunc and is returning a 401 but I haven’t worked out why yet) e.g.

      authorizer:
        name: serviceBAuthFunc
        arn: ${cf:service-a-${self:provider.stage}.AuthFunc}

I know this is closed but thought I’d chip in for anybody stuck on this issue using a Cognito authorizer.

All you need to do is replace SHARED_SERVICE_NAME, REGION, ACCOUNT_ID & USER_POOL_ID with your own values.

serverless.yml:

service: SHARED_SERVICE_NAME
...
resources:
  Resources:
    SharedApiGatewayAuthorizer:
      Type: AWS::ApiGateway::Authorizer
      Properties:
        Name: cognito-${opt:stage, self:provider.stage}
        Type: COGNITO_USER_POOLS
        IdentitySource: method.request.header.Authorization
        ProviderARNs:
          - arn:aws:cognito-idp:REGION:ACCOUNT_ID:userpool/USER_POOL_ID
        RestApiId:
          Ref: ApiGatewayRestApi
  Outputs:
    apiGatewayAuthorizerId:
      Value:
        Ref: SharedApiGatewayAuthorizer
      Export:
        Name: apiGateway-authorizerId-${opt:stage, self:provider.stage}

Lambda function reference to shared authorizer:

  events:
    - http:
        ...
        authorizer:
          type: COGNITO_USER_POOLS
          authorizerId: '${cf:SHARED_SERVICE_NAME-${opt:stage, self:provider.stage}.apiGatewayAuthorizerId}'

This could be solved if it was possible to simply reference an Authorizer by its ID: (AWS::ApiGateway::Method -> AuthorizerId)

I cannot find a way to do that. Is there any plugin or something that does that? Because from a code perspective this should be relatively easy.

same as above but a bit less verbose. I hit an issue trying to use !Ref & the like under the function declaration. Turns out all I needed was to define Ref: <CREATED RESOURCE> under authorizerId.

functions:
  routes:
    handler: handler.stuff
    events:
      - http:
          method: get
          path: stuff
          authorizer: 
            type: COGNITO_USER_POOLS
            authorizerId:
              Ref: UserPoolAuthorizer      <-- <CREATED RESOURCE>

( with the Resource creation looking like )

Resources:
  UserPoolAuthorizer:                      <-- <CREATED RESOURCE>
    Type: AWS::ApiGateway::Authorizer
    Properties:
      Name: UserPoolAuthorizer
      ProviderARNs: 
        - ${self:custom.UserPoolArn}
      Type: COGNITO_USER_POOLS
      IdentitySource: method.request.header.Authorization
      RestApiId: !Ref ApiGatewayRestApi

So, to work around the issue I’ll need to implement the shared authorizer approach. The example is for Cognito authorizer. Can someone help me with one that would work with a Lambda authorizer (similar to the one described here?..

I seem to still be running into this issue with the new HTTPApi authorizers. For me, I have a shared service that defines a common API with:

    SharedApiGatewayAuthorizer:
      Type: AWS::ApiGatewayV2::Authorizer
      Properties:
        Name: myAuthorizer
        ApiId: !Ref httpApi # (httpApi defined earlier in service)
        AuthorizerType: JWT
        IdentitySource:
          - $request.header.Authorization
        JwtConfiguration:
          Audience:
            - XXXXXXXX
          Issuer: https://XXXXXXXXX

  Outputs:
    apiAuthorizerId:
      Value: !Ref SharedApiGatewayAuthorizer

Then, in my other service, I’m attempting to reference it (after successfully deploying the above) with:

functions:
  validate:
    handler: handler.validate
    events:
      - httpApi:
          path: /validate
          method: GET
          authorizer:
            type: JWT
            authorizerId: ${cf:shared-api-gateway-${opt:stage}.apiAuthorizerId}

But no luck. I get the error: Event references not configured authorizer 'undefined'.

I’ve tried everything suggested above with no luck 😦

I’ve just spotted https://github.com/serverless/serverless/issues/7598 which seems to describe this issue too.

@jackrk Hey Jack, thanks for the slight “push” and information 👍 I will check it and do a merge… maybe it can go straight into the 1.27.3 which will be available in the next few days.