Skip to content

Cannot reference an authorizer already created within services that shares the same API GW #4711

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

Closed
mpaleo opened this issue Feb 3, 2018 · 28 comments · Fixed by #4197 · May be fixed by #7017
Closed

Cannot reference an authorizer already created within services that shares the same API GW #4711

mpaleo opened this issue Feb 3, 2018 · 28 comments · Fixed by #4197 · May be fixed by #7017
Milestone

Comments

@mpaleo
Copy link

mpaleo commented Feb 3, 2018

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:
@pfried
Copy link

pfried commented Feb 7, 2018

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.

@idwright
Copy link

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}

@carlosvictor
Copy link

Same problem here.

Anyone with a solution ?

@ftrevo
Copy link

ftrevo commented Mar 15, 2018

Same problem here too.

@lucaspitang
Copy link

I'm searching for any solution for this problem, anyone could give me some help?

@jacintoArias
Copy link

Has anybody tested this PR to solve this issue? #4197

I know it only references COGNITO_USER_POOLS but perhaps it might work as well with Custom authorizers...

@anelmu
Copy link

anelmu commented Apr 17, 2018

@jacintoArias I have tested #4197 with custom authorizer and works with multiple services.
Only needed to change type from COGNITO_USER_POOLS to CUSTOM.

@rohitshetty
Copy link
Contributor

I hit the same problem, I just gave a name to my authorizer like @idwright Suggested, and it seems to be working. Anyone have any idea if this is the correct way (at least until a fix is released?) Or if we can have any caveats?

@rohitshetty
Copy link
Contributor

Adding name, creates different authorizer in the api gateway for each lambda.
I am not sure if this is a good practice? Also Not sure if there is any upper limit on this. Please let me know.

@jackrk
Copy link

jackrk commented May 22, 2018

This is Jack from the API Gateway team. Please merge #4197 as a fix. This is painful for customers because there is a limit of 10 authorizers per RestApi, and they are forced to contact AWS to request a limit increase to unblock development. Thank you!

@HyperBrain
Copy link
Contributor

@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.

@rohitshetty
Copy link
Contributor

Thank you @jackrk and @HyperBrain I am one of the customers Jack mentioned above. I am glad to hear this will be available in 1.27.3! Thank you for your efforts.

@HyperBrain HyperBrain added this to the 1.27.3 milestone May 23, 2018
@fedebalderas
Copy link

I still have this issue with custom authorizers in v1.35.0... any idea?

@rohitshetty
Copy link
Contributor

@fedebalderas Can you share more details?

@fedebalderas
Copy link

@rohitshetty never mind, i didn't see this approach https://serverless.com/framework/docs/providers/aws/events/apigateway#share-authorizer

@roni-frantchi
Copy link

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?..

@erksdee
Copy link

erksdee commented Apr 3, 2019

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?..

Hi @roni-frantchi, were you able to achieve this with auth0 ?

@roni-frantchi
Copy link

Hi @erksdee . Not yet. Had to park it for a few days unfortunately as priorities have shifted.
Will definitely post an update here once I'll be able to figure out how to.

@pscadiz
Copy link

pscadiz commented May 24, 2019

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

@pirahana
Copy link

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

Hi @pscadiz , i am also facing the same problem. Can you help me understand, what does 'RestApiId' and 'DependsOn' keys mean in the resources object. I am tring to use a lambda authorizer in a different region. My serverless.yml looks like:

provider:
name: aws
runtime: nodejs10.x
stage: dev
region: us-east-1

functions:
app:
handler: app.server # reference the file and exported method
events: # events trigger lambda functions
- http: # this is an API Gateway HTTP event trigger
path: /
method: ANY
cors: true
- http: # all routes get proxied to the Express router
path: /{proxy+}
method: ANY
authorizer:
name: AuthorizerWithEnv
arn: aws:lambda:us-east-2:XXXXXXXXX:function:AuthorizerWithEnv
identitySource: method.request.header.Authorization
type: token

But whenever is deploy this, I get the following error :

An error occurred: AuthorizerWithEnvApiGatewayAuthorizer - Invalid lambda function (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: bd1622de-8da7-11e9-a20b-4b63dfdcb095).

Do you have any suggestions ?

@pscadiz
Copy link

pscadiz commented Jun 13, 2019

@pirahana

RestApiId: !Ref "ApiGatewayRestApi" and DependsOn: - ApiGatewayRestApi are actually workarounds that make use of the cloudformation templates generated by serverless. When you deploy a serverless project, it generates cloudformation stacks and run them (you'll see this in .serverless/cloudformation-template*.json in your root directory) When you set a function with events, sls creates an APIGW Rest API, with which the identifier is "ApiGatewayRestApi"

So what happened was I created an authorizer and placed it in the API that sls generated, the DependsOn ensures that the Rest API is created first before the authorizer is created, otherwise it will just fail. Then the authorizer is added to the functions through the CUSTOM type instead.

As per the documentation, we can't use the name in authorizer since that assumes that the authorizer is a function declared (in functions:) in the sls project itself, and we can't use arn as well since that assumes that the authorizer is a lambda function in the same region.

So the workaround is:

  1. Declare an authorizer through sls.yaml's resources section, pointing to the lambda function in the other region's invocation path in AuthorizerId
  2. Add the authorizer through the functions by using type CUSTOM

I hope i made sense.

@pirahana
Copy link

Hi @pscadiz , thanks for the prompt reply. I added the Lambda authorizer in the same region and it worked. The serverless.yml files is like:

custom:
authorizer:
arn: arn:aws:lambda:us-east-1:XXXXXXX:function:testAuthForeast1
resultTtlInSeconds: 0
identitySource: method.request.header.Authorization
identityValidationExpression: '.*'

provider:
name: aws
runtime: nodejs10.x
stage: dev
region: us-east-1

functions:
app:
handler:
events:
- http:
path: /
method: ANY
cors: true
- http:
path: /{proxy+}
method: ANY
authorizer: ${self:custom.authorizer}

This works, but this is not what i want. I want that lambda authorizer from any account/any region can be made accessible.
If I make the
arn path as: arn:aws:lambda:us-east-2:XXXXXXX:function:testAuthForeast1
The i get the following error:

An error occurred: AuthorizerWithEnvLambdaPermissionApiGateway - Functions from 'us-east-2' are not reachable in this region ('us-east-1') (Service: AWSLambda; Status Code: 404; Error Code: ResourceNotFoundException; Request ID: 4f668318-8dc6-11e9-ac56-b900da7dc499).

I hope you are able to understand that I dont want it to access in the same region, but I am unable to access other region authorizer. Do you have any suggestions regarding the same ?
Thanks !

@cameljava
Copy link

hi, Guys:

I am quite new to serverless, just wondering if we can deploy a customer authoriser an individual lambda instead of having to deploy with a service in api gateway?

Thanks

@atz
Copy link
Contributor

atz commented Sep 26, 2019

@cameljava The gateway is the AWS layer that has knowledge of both lambdas and authorizers. A lambda in isolation, afaict, does not have the ability to describe a prerequisite (authorizer, validator, etc.). This difference is evident in the respective API Gateway and Lambda consoles.

KostiantynKopytov added a commit to KostiantynKopytov/serverless that referenced this issue Nov 27, 2019
…mation

now authorizers will get auth lambda name that usually includes service/stage name or customized instead of  property name
@Jordan-Eckowitz
Copy link

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}'

@nicovigil1
Copy link

nicovigil1 commented Jan 27, 2020

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

@benheymink
Copy link

benheymink commented Apr 30, 2020

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 #7598 which seems to describe this issue too.

@kaunglvlv
Copy link

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}

This worked for me. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment