How to create a custom authorizer for AWS API Gateway using serverless Lambda functions in Node and .NET Core

How to create a custom authorizer for AWS API Gateway using serverless Lambda functions in Node and .NET Core

Going “serverless” is now becoming one of today’s hot trends for cloud solutions. It’s an agile, modern architectural approach that can help businesses perform faster, better and cheaper.

In serverless cloud solutions, the provider manages the servers and resources to host applications, and charge customers on a flexible “pay for what you use” model. This reduces the need for manual resource provisioning and maintenance, which lets developers focus on value-adding projects.

I first started working with serverless solutions in AWS using Lambda functions, and now I’m utilizing Lambda functions on a regular basis.

Why Use Lambda Functions?

A Lambda function is really a combination of two things:

  1. A triggering management mechanism for when/why it should run
  2. A piece of code, which is equivalent of calling a function in a DLL, a web-service through an API, or a script through a cell in Excel

A function runs as your code, so it’s not as public as an API or Windows DLL. They’re linked with dedicated cloud resources (i.e. dedicated RAM and CPU), and payment can be either pay-per-use or pay-per-instance.

A function can also be spun as many times as needed, as opposed to many DLLs that are called once, or APIs for which whatever happens in the background is transparent for the user.

One of the services that integrates well with Lambda functions is the AWS API Gateway.  When used together you can build secure serverless APIs.

When using AWS API Gateway, we have many options to secure our APIs. One feature that AWS provides is the ability to create custom authorizers.

What is a Custom Authorizer?

A custom authorizer is basically a Lambda function that you create to provide control access to your API methods. It is a way to secure your APIs by validating data and requests before they are processed.

Custom authorizers use bearer token authentication strategies such as OpenID, OAuth, SAML, or AWS Cognito.

The basic flow of the custom authorizer follows this:

  1. A client will make a request to your API.
  2. The API Gateway will determine if a custom authorizer is configured and will invoke it.
  3. The custom authorizer will then determine if the token is valid and generate a policy.
  4. The API Gateway will check the policy and will either “allow” or “deny” your request to the API.

custom authorizer aws api gateway flow diagram

Note:  Additional flow information can be found here.

In this tutorial, I will show you how to create a custom authorizer, an API Lambda function using .NET Core, and configure the API Gateway to work with your custom authorizer.

Let’s get started.

Prerequisites

Before we begin this tutorial, you will need the following:

  • AWS
  • Visual Studio 2017 or above
  • Visual Studio AWS Toolkit

Now that you have the right tools, let’s build our custom authorizer.

This tutorial will follow 7 steps:

  1. Create an API Lambda Function using ASP.NET Core Web API
  2. Deploy the API Lambda Function to AWS
  3. Test the API Gateway with the API Lambda Function
  4. Create a Custom Lambda Authorizer Function
  5. Create an Authorizer for the API in the API Gateway
  6. Deploy the Authorizer for the API in the API Gateway
  7. Test the API Gateway

Step 1: Create an API Lambda Function using ASP.NET Core Web API

Let’s start with creating the ASP.NET Core Web API in Visual Studio.

Open Visual Studio and from the project menu create a new project.   The dialog below will open.

Go head and click the “AWS Lambda” and select the “AWS Serverless Application (.NET Core)” and name it.

screenshot create api lambda function using asp.net core web api

Now select the “ASP.NET Core Web API” Blueprint.  This will create a Lambda project template using ASP.NET Core.

screenshot select AWS serverless application blueprint

Once the project is created you will see the following code structure.  Since this is just a basic tutorial, we will use the default API generated code to deploy and test.

The API class we use will be the “ValuesController.cs”, which has basic HTTP Methods “GET, POST, PUT, DELETE”.   You can also update the methods with your own logic.

screenshot value controller test

Run the project and test the API locally.  Make sure you use the path http://localhost:{port}/api/values to test.

You should see the response below:

api values to test report

Step 2: Deploy the API Lambda Function

Now that the project has been created and is working locally, let’s publish it to AWS.   Right click on the project and select “Publish to AWS Lambda…”

screenshot publish to aws lambda

Fill out the required profile information and build settings.  Then click “Next” to verify addition settings, then click “Publish”.

screenshot publish aws serverless application

Step 3: Test the API Gateway with the API Lambda Function

After a successful publish to AWS, the console in AWS will report the status of the stack as “CREATE_COMPLETE” and create the AWS Serverless URL where the API can be accessed.

screenshot test api gateway with api lambda function

Now copy the base URL and test in your browser.  You should get the same response as you did locally.

Note: Make sure to add your full API path in your URL.

screenshot test api gateway with api lambda response

Your API is now successfully running in your AWS API Gateway.

Step 4: Create a Custom Lambda Authorizer Function

With your API running in AWS, let’s create a custom Lambda Authorizer.

Go to Services->Lambda and create a new function.  Then input the following:

  1. Select “Author from scratch”
  2. Name of your Lambda function
  3. Runtime: Node.js 6.10
  4. Select a role or existing role.
  5. Click “Create Function”

screenshot create custom lambda function

In your Lambda function, scroll down to your Function code editor and add the following code to the index.js file and save your function.

screenshot create custom lambda function example code


// A simple TOKEN authorizer example to demonstrate how to use an authorization token
// to allow or deny a request. In this example, the caller named 'user' is allowed to invoke
// a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke
// the request if the token value is 'deny'. If the token value is 'Unauthorized', the function
// returns the 'Unauthorized' error with an HTTP status code of 401. For any other token value,
// the authorizer returns an 'Invalid token' error.

exports.handler = function (event, context, callback) {
    var token = event.authorizationToken;
    switch (token.toLowerCase()) {
        case 'allow':
            callback(null, generatePolicy('user', 'Allow', event.methodArn));
            break;
        case 'deny':
            callback(null, generatePolicy('user', 'Deny', event.methodArn));
            break;
        case 'unauthorized':
            callback(null, generatePolicy('user', 'Allow', event.methodArn));
            break;
        default:
            callback('Error: invalid token');
    }
};

// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    var authResponse = {};

    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17';
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke';
        statementOne.Effect = effect;
    }
}

Note:

One point I want to highlight in the code is the following line:

callback(null, generatePolicy('user', 'Allow', '*'));

The generatePolicy method has the ‘*’ parameter passed in.  This is a wildcard to cover all methods in the policy, since the API is configured as a Lambda proxy.   This is very important, because you will run into issues for different API method requests if the wildcard is not used.

In addition, when caching is enabled on the authorizer, you will avoid “User is not authorized to access this resource” errors.

When you only want to use a specific method (i.e. GET) you want to use the  ‘event.methodArn’ instead of the wildcard.

Step 5: Create an Authorizer for the API in the API Gateway

Let’s now go back to the API Gateway service and select your API and then select “Authorizers”.  Here you will create a new authorizer.    Fill in the following and click “Create”:

Name: {authorizer name}

Type: Lambda

Lambda Function: {Select the name of the one you create in the previous section}

Lambda Event Payload: Token

Token Source: {Token Header you want to use to test}

Authorization Caching: {optional if you want caching}

screenshot create authorizer

Now select “Resources” and click on “ANY”. Here you will see the Method Execution information and you will also notice in the “Method Request” box has no Auth.   You will now need to click on “Method Request”.

screenshot method execution

In the “Method Request”, you will now select the “Authorization” that you created.  Make sure you click on the circle check box so it will save your settings.

method authorization

Step 6: Deploy the Authorizer for the API in the API Gateway

Go to the “Actions” drop down and select “Deployment stage” and click “Deploy”.

screenshot deploy authorizer for api in api gateway

Step 7: Test API Gateway

We are now ready to test our API and verify the custom authorizer is working.   Go to your API’s published URL and test it.

You should get an unauthorized message, which is good because we know the authorizer has been executed and is validating if the header is provided.

screenshot test api gateway response

Let’s add the header “test-token” to our request.  You can use any modify header browser plugin to assist you with this or a popular tool like Postman.

Once you add the header, you can now make the same request again and see a successful response.

screenshot test token response

Finished

Now that you have completed this tutorial, you can go on to create your own custom authorizer to perform secure token-based authorization in AWS.   I hope these steps help you on your next authorizer.

For more advanced authorizers, you can also integrate with third party identity service providers such as Auth0. I’ll cover these in my next tutorial.

 

Learn more:

Securing AWS API Gateway Endpoints with Custom Authors

How API Gateway Resource Policies Affect Authorization Workflow