Shared Lambda authorizer setup in Serverless Framework

I am trying to create a custom Lambda authorizer that will be shared between a few different services/serverless stacks. If I understand the documentation here https://serverless.com/framework/docs/providers/aws/events/apigateway/#note-while-using-authorizers-with-shared-api-gateway, that means that I need to create a shared authorizer resource in a “common resources” service/serverless stack, and then refer to that shared authorizer from my other services. First of all: Is my understanding correct?

If my understanding is correct, my next question becomes: How do I do this? The documentation doesn’t provide a clear example for lambda authorizers, so here’s how I tried to customize it:

functions:
authorizerFunc:
handler: authorizer/authorizer.handler
runtime: nodejs8.10

resources:
Resources:
authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
Name: Authorizer
Type: REQUEST
AuthorizerUri: ???
RestApiId:
Fn::ImportValue: myRestApiId

I don’t understand what the syntax for AuthorizerUri is supposed to be. I’ve tried “Ref: authorizerFunc”, “Fn::GetAtt: [authorizerFunc, Arn]” etc. to no avail.

When I get the authorizerUri working, do I just add an Output for my authorizer resource, then Fn::ImportValue it from the services containing my API Lambdas?

Link to my question on the Serverless forum for posterity: https://forum.serverless.com/t/shared-lambda-authorizer/6447

Answers 1

  • EDIT: Apparently my answer is now outdated. For newer versions of servicestack, see the other answers. I don't know which answer is best/most up-to-date, but if someone lets me know I'll change which answer is accepted to that one.

    I eventually got it to work, so here's how I set up my autherizer's serverless.yml:

    service: user-admin-authorizer
    
    custom:
      region: ${file(serverless.env.yml):${opt:stage}.REGION}
    
    provider:
      name: aws
      region: ${self:custom.region}
    
    functions:
      authorizer:
        handler: src/authorizer.handler
        runtime: nodejs8.10
    
    resources:
      Resources:
        Authorizer:
          Type: AWS::ApiGateway::Authorizer
          Properties:
            Name: Authorizer
            Type: REQUEST
            AuthorizerUri:
              Fn::Join: [ "",
                [
                  "arn:aws:apigateway:",
                  "${self:custom.region}",
                  ":lambda:path/",
                  "2015-03-31/functions/",
                  Fn::GetAtt: ["AuthorizerLambdaFunction", "Arn" ],
                  "/invocations"
                ]]
            RestApiId:
              Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
        apiGatewayLambdaPermissions:
          Type: AWS::Lambda::Permission
          Properties:
            FunctionName:
              Fn::GetAtt: [ AuthorizerLambdaFunction, Arn]
            Action: lambda:InvokeFunction
            Principal:
              Fn::Join: [ "",
              [
                "apigateway.",
                Ref: AWS::URLSuffix
              ]]
    
      Outputs:
        AuthorizerRef:
          Value:
            Ref: Authorizer
          Export:
            Name: authorizer-ref:${opt:stage}
    
    

    Things to note: Even though the authorizer function is called "authorizer", you need to capitalize the first letter and append "LambdaFunction" to its name when using it with GetAtt, so "authorizer" becomes "AuthorizerLambdaFunction" for some reason. I also had to add the lambda permission resource.

    The API gateway resource also needs two outputs, its API ID and its API root resource ID. Here's how my API gateway's serverless.yml is set up:

    resources:
      Resources:
        ApiGateway:
          Type: AWS::ApiGateway::RestApi
          Properties:
            Name: ApiGateway
    
      Outputs:
        ApiGatewayRestApiId:
          Value:
            Ref: ApiGateway
          Export:
            Name: api-gateway:${opt:stage}:rest-api-id
        ApiGatewayRestApiRootResourceId:
          Value:
            Fn::GetAtt:
              - ApiGateway
              - RootResourceId
          Export:
            Name: api-gateway:${opt:stage}:root-resource-id
    

    Now you just need to specify to your other services that they should use this API gateway (the imported values are the outputs of the API gateway):

    provider:
      name: aws
      apiGateway:
        restApiId:
          Fn::ImportValue: api-gateway:${opt:stage}:rest-api-id
        restApiRootResourceId:
          Fn::ImportValue: api-gateway:${opt:stage}:root-resource-id
    

    After that, the authorizer can be added to individual functions in this service like so:

              authorizer:
                type: CUSTOM
                authorizerId:
                  Fn::ImportValue: authorizer-ref:${opt:stage}
    

Related Articles