Creating .Net Lambda Authorizer for AWS API Gateway

 



.Net Lambda Authorizer for AWS API Gateway

Introduction

When we visit any site (for instance, www.blogger.com), we can access its resources; anybody can access it, and there is no need to protect it. However, when we want to access our account, the resources need to be protected. To protect the unauthorized access, we have to use Authorizer. We have seen in this post how we can use JWT token authorizer to protect our API. But, Amazon Web Services have simplified it by providing us an Authorizer. To know some basics about AWS Lambda and API Gateway, please read below:

When we develop a service, we have to host it on a server (for AWS it's EC2). But, many times our service (usually microservice) will do a simple job such as showing up some static data to the user. It does not make sense to create a server to deploy it on our server (EC2). Instead, we need a tiny server that can manage itself. When I say manage it includes many tasks such as starting the server, handling requests, or stopping the server. This is is the concept of serverless applications. This tiny server is called Lambda in AWS. When we deploy our service to Lambda, we need to expose the API to the public, and we do it using AWS's API Gateway. The APIs need to be protected from any unauthorized access and to protect, we use API Gateway's Authorizer

 

There are two types of Authorizer available:

a. Token-based: In this case, the HTTP request should have a Bearer token in the HTTP request's authorization header.

b. Request-based: In this case, the HTTP request will have custom headers (one or many).

In this example, we shall use Request-based.


Prerequisites:

a. You need an AWS account (free-tier). 

b. Visual Studio 2019.

c. Install AWS toolkit in Visual Studio 2019.


Steps to create a .Net Lambda Authorizer

In the below steps, we shall create a .Net Lambda authorizer. This will have a basic authentication: we shall pass username and password. This is not recommended for production code. For the production code, use JWT instead of basic authentication.

a. Create a new project in Visual Studio, and if you had installed AWS toolkit, you would see the below template. Select AWS Lambda Project (.Net Core - C#).



b. Select Empty Serverless Application as below:



c. The project will be created, and you would see serverless.  Template file. This file will have the below entry:

"Handler": "LambdaAuthorizer::LambdaAuthorizer.Functions::Get",

The above line is the Lambda entry point, i.e., this is the first method the Lambda invokes.

This method looks as below:

   public APIGatewayProxyResponse Get(APIGatewayProxyRequest request, ILambdaContext context)
        {
            context.Logger.LogLine("Get Request\n");
            var response = new APIGatewayProxyResponse
            {
                StatusCode = (int)HttpStatusCode.OK,
                Body = "Hello AWS Serverless",
                Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
            };
            return response;
        }

d. So, in this method, we need to read the headers pass in the HTTP requests and check for the correct username and password. If this is wrong, we need to return Deny, else it will return Allow.

e. add the below code to read the headers. We are reading userName and the passWord headers.

            var item = string.Empty;
            try
            {
                item = request.Headers.Where(x => String.Equals(x.Key, "userName", StringComparison.InvariantCultureIgnoreCase))
                    .Select(p => p.Value).Single();
            }
            catch (Exception e)
            {
                context.Logger.LogLine("$Exception occured when reading userName header : {e}");
                item = "";
            }

            var item = string.Empty;
            try
            {
                item = request.Headers.Where(x => String.Equals(x.Key, "passWord", StringComparison.InvariantCultureIgnoreCase))
                    .Select(p => p.Value).Single();
            }
            catch (Exception e)
            {
                context.Logger.LogLine("$Exception occured when reading passWord header : {e}");
                item = "";
            }
            string passWord = item.ToString();

f. Next, if you notice point c, the Get method returns APIGatewayProxyResponse and accepts APIGatewayProxyRequest. Since we are developing a .Net AUthorizer, we need to return APIGatewayCustomAuthorizerResponse, and it should accept  APIGatewayCustomAuthorizerRequest.Hence, change the return value as below:

 public APIGatewayCustomAuthorizerResponse Get(APIGatewayCustomAuthorizerRequest request, ILambdaContext context)

g. We have to read the header, and we need to check the header for the right values. If the values are correct, we need to return Allow; else, we need to return Deny.

The code is as below:

 #region checkHeaders
            if(userName.Equals("chaiandwine") && passWord.Equals("pass"))
            {
                APIGatewayCustomAuthorizerPolicy policy = new APIGatewayCustomAuthorizerPolicy
                {
                    Version = "2012-10-17",
                    Statement = new List<APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement>()
                };
                policy.Statement.Add(new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement
                {
                    Action = new HashSet<string>(new string[] { "execute-api:Invoke" }),
                    Effect = "Allow",
                    Resource = new HashSet<string>(new string[] { request.MethodArn })
                });

                APIGatewayCustomAuthorizerContextOutput contextOutput = new APIGatewayCustomAuthorizerContextOutput();
                contextOutput["User"] = "User";
                contextOutput["Path"] = request.MethodArn;
                return new APIGatewayCustomAuthorizerResponse
                {
                    PrincipalID = "User",
                    Context = contextOutput,
                    PolicyDocument = policy
                };
            }

            #endregion

            #region response
            APIGatewayCustomAuthorizerPolicy newPolicy = new APIGatewayCustomAuthorizerPolicy
            {
                Version = "2012-10-17",
                Statement = new List<APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement>()
            };
            newPolicy.Statement.Add(new APIGatewayCustomAuthorizerPolicy.IAMPolicyStatement
            {
                Action = new HashSet<string>(new string[] { "execute-api:Invoke" }),
                Effect = "Deny",
                Resource = new HashSet<string>(new string[] { request.MethodArn })
            });

            APIGatewayCustomAuthorizerContextOutput newContextOutput = new APIGatewayCustomAuthorizerContextOutput();
            newContextOutput["User"] = "User";
            newContextOutput["Path"] = request.MethodArn;
            return new APIGatewayCustomAuthorizerResponse
            {
                PrincipalID = "User",
                Context = newContextOutput,
                PolicyDocument = newPolicy
            };
            #endregion

Out Lambda Authorizer is ready now. We need to deploy this Lambda in the AWS and configure the Lambda as Authorizer.

To know the steps to deploy any .Net Lambda to AWS, please refer to this link - https://www.chaiandwine.info/2021/03/deploying-net-lambda-to-aws.html .

To configure the Lambda as Authorizer, please check the below steps:

a. In the AWS console, navigate to API Gateway service and click Create API.


b. In the next screen, select Rest API and click Build.

c. Provide a name and select Endpoint Type as Regional. Click Create API.




d. In the left Panel, click Authorizer and click Create New Authorizer.




e. Enter details as following:




f. Click Create, and it will ask for the permissions. Please click on Grant.

g. Authorizer is now created. Click on the test button. We would see the below screen:


h. Enter details as below:



Since we entered the wrong password, we will get a response as Deny.

Let's try with the correct password:



The next steps should be to configure the Get method in the API Gateway and configure the above Lambda as the Authorizer. After deploying the API, you could test in the Postman. Since this is beyond the concept of Authorizer concept, I am not writing about that here.

Next recommended Article- aws-cross-account authorizer

Happy coding 😊