1
votes

I am creating a serverless API using AWS SAM template and ASP.Net Core. I wanted to know if it is possible to call a common lamnda function from multiple lambda functions?

I have 2 APIs for user authentication.

  1. /user/authenticate
  2. /admin/authenticate

Now when the user calls these API endpoints I want to call a common lambda function which will look like following:

public AuthResponse Authenticate(AuthInfo info, int role);

I get a user role based on which API endpoint is called. For example if /user/authetication is called then role=1 otherwise role=0. And then I want Authenticate() lambda to perform user authentication based on the AuthInfo + Role.

I want to do this because all my users are stored in the same table and I would like to cross verify if user has the correct role to access the feature.

I will also share a portion of serverless.template used for above APIs.

/admin/authenticate

"Handler": "Server::API.Admin::Authenticate",
        "Description" : "Allows admin to authenticate",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "MemorySize": 256,
        "Timeout" : 300,
        "Role": {"Fn::GetAtt" : [ "LambdaExecutionRole", "Arn"]},
        "FunctionName" : "AdminAuthenticate",
        "Events": 
        {
          "PutResource": 
          {
            "Type": "Api",
            "Properties": 
            {
              "Path": "/v1/admin/authenticate",
              "Method": "POST"
            }
          }
        }
      }
    }

/user/authenticate

"Handler": "Server::API.User::Authenticate",
        "Description" : "Allows user to authenticate",
        "Runtime": "dotnetcore2.1",
        "CodeUri": "",
        "MemorySize": 256,
        "Timeout" : 300,
        "Role": {"Fn::GetAtt" : [ "LambdaExecutionRole", "Arn"]},
        "FunctionName" : "UserAuthenticate",
        "Events": 
        {
          "PutResource": 
          {
            "Type": "Api",
            "Properties": 
            {
              "Path": "/v1/user/authenticate",
              "Method": "GET"
            }
          }
        }
      }
    }

As you can see above, 2 lambda functions are created AdminAuthenticate and UserAuthentication. I want these lambda functions to share the common code.

Does anyone has any idea how to do it?

Thanks and Regards.

2

2 Answers

0
votes

I can think about 2 options to achieve your goal. In the first option, you use multiple Lambda functions, one for each endpoint, both pointing to your same codebase. In the second option, you have a single Lambda function that handles all authentication needs.

Single codebase, multiple functions

In this case, you can define your template file with 2 functions but use the CodeUri property to point to the same codebase.

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Transform": "AWS::Serverless-2016-10-31",
    "Resources": {
        "AdminFunction": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "Handler": "Server::API.Admin::Authenticate",
                "Description": "Allows admin to authenticate",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "./codebase_path/",
                "MemorySize": 256,
                "Timeout": 300,
                "FunctionName": "AdminAuthenticate",
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/v1/admin/authenticate",
                            "Method": "POST"
                        }
                    }
                }
            }
        },
        "UserFunction": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "Handler": "Server::API.User::Authenticate",
                "Description": "Allows user to authenticate",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "./codebase_path/",
                "MemorySize": 256,
                "Timeout": 300,
                "FunctionName": "UserAuthenticate",
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/v1/user/authenticate",
                            "Method": "POST"
                        }
                    }
                }
            }
        }
    }
}

Single codebase, single function

In this case, you will expose 2 endpoints on API Gateway, but they will be directed to the same handler on your function. Therefore, you will need to write some logic on your code to handle the login properly. The event object passed to your Lambda function will have information on the original URL in the path property (reference -- even though this is for Lambda proxy, still applies).

The template file in this case would be similar to (note I replaced Admin/User terms to "Any", since this will handle any form of authentication):

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Transform": "AWS::Serverless-2016-10-31",
    "Resources": {
        "AnyFunction": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "Handler": "Server::API.Any::Authenticate",
                "Description": "Allows any to authenticate",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "./hello_world/",
                "MemorySize": 256,
                "Timeout": 300,
                "Events": {
                    "UserEndpoint": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/v1/user/authenticate",
                            "Method": "POST"
                        }
                    },
                    "AdminEndpoint": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/v1/admin/authenticate",
                            "Method": "POST"
                        }
                    }
                }
            }
        }
    }
}
0
votes

You can invoke lambda functions through any lambda function, using aws sdk in your chosen language and there you have invoke function defined. For a reference here is the link to boto3 invoke definition.

Also

The approach you are using for authentication using common codebase is not the right one.

If you need a lambda function to check or authentication particular request, you can setup custom authorizer for that, which in your terms i can say, share lambda code or call it first before invoking the lambda you setup for the particular endpoint with the possibility to pass the custom data if you want.

A Lambda authorizer (or as a custom authorizer) is an API Gateway feature that uses a Lambda function to control access to your API.

enter image description here

If still this doesn't solve your problem and you want common codebase, you can point out as many as api endpoint's to same lambda function. Then you have to handle event['resources'] inside your codebase. This is not the recommended way, but you can use it.

You can refer aws samples to setup custom authorizer or the documentation is fair enough to clear all your doubts.