The goal of this tutorial is to return a “Hello World” if you connect and authenticate successfully to our 100% serverless application. You will discover in this article how to take advantage of AWS Cognito, deploy an AWS API Gateway and a few lambda functions through the serverless.com framework.

AWS API gateway, Amazon Cognito, and AWS Lambda working together

Installation

The first step is to install ServerlessPython3 & Boto3 (to allow use of Cognito with Python), Postman, and AWS CLI.

NPM

NPM (Node Package Manager) needs to be installed before installing Serverless. NPM is installed with Node.js. The following tutorial guides you through the installation: https://www.ostechnix.com/install-node-js-linux/

Serverless

Serverless is used to deploy the AWS API Gateway and other resources such as Cognito and the lambdas that are created using Python3. The tutorial on how to install Serverless can be found here: https://serverless.com/framework/docs/getting-started/

Python3

We have used Python3 in this example to create lambda functions. Other languages such as Javascript or Golang can be used for this as well. The following guide walks you through the installation and setup of Python3: https://realpython.com/installing-python/

You’ll have to execute a command line that works for the OS being used. For example, on Arch Linux we have to execute the following:


gt; pacman -S python

Once Python3 has been installed, you can use the following command to check whether Python has been successfully downloaded:


gt; python3 –version

Boto3

Boto3 is a Software Development Kit (SDK) that enables Python developers to create, configure, and manage AWS services such as EC2 and S3. For Boto3, you’ll need a PIP (which is generally installed with Python). You can use the following command to check if the PIP exists:


gt; pip –version

If an error is returned, you will have to install it. Here is a guide that walks you through this process: https://pip.pypa.io/en/stable/installing/

Once you have installed the PIP, execute the following command:


gt; pip install boto3

Postman

Postman is an easy-to-use software that helps build, test, and modify APIs. Postman has the ability to make various types of HTTP requests to an API. Here is a link to Postman’s website where the platform can be downloaded: https://www.getpostman.com/

AWS CLI

AWS CLI (Command Line Interface) must also be installed to manage your AWS services and to receive Cognito tokens. The following tutorial details how to install it: https://docs.aws.amazon.com/cli/latest/userguide/install-linux-al2017.html


gt; aws –version
aws-cli/1.16.253 Python/3.7.3 Linux/4.19.56–1-MANJARO botocore/1.12.243

Serverless — Simple Configuration

Begin by configuring Serverless. Serverless is a tool that can be used with AWS to deploy resources and APIs. If you want to investigate serverless in more detail here is the documentation: https://serverless.com/framework/docs/providers/aws/

Create a Serverless project

The following command (in Python) creates a Serverless project:


gt; serverlessServerless: No project detected. Do you want to create a new one? Yes
Serverless: What do you want to make? AWS Python
Serverless: What do you want to call this project? trackit
Project successfully created in ‘trackit’ folder.You can monitor, troubleshoot, and test your new service with a free Serverless account.Serverless: Would you like to enable this? No
You can run the “serverless” command again if you change your mind later.

Based on what we’ve entered, a folder called ‘trackit’ (name of the project) is created. This newly created ‘trackit’ folder should have a Python file (which you can remove for now) and a file called serverless.yaml; this is your Serverless configuration file.

Simple configuration

Once the project is created, the serverless.yaml file needs to be configured. Here is a simple example of a Serverless configuration:

org: trackitprovider:
 name: aws
 runtime: python3.7
 region: us-west-2
 timeout: 60functions:
 test_trackit:
 handler: test.endpoint_test
 events:
  — http:
   path: test
   method: get

As you can see in the code above, the name entered after service: will be the name of your AWS API Gateway.

You must also set up the name of your provider. In this example, we’re using AWS with Serverless so the name of the provider is AWS. We’re using Python 3.7 as our runtime (our lambdas will use Python 3.7).

You also have to choose your region; the following guide by AWS on Regions and Availability zones will provide you with some guidance: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)

In this example, we’re looking to deploy in Oregon so the region we’ve selected is us-west-2 and we set a timeout of 60 seconds (you might want more or less based on your requirements).

We then create a simple function called endpoint_test which will be located in the test.py file. We have decided to call this lambda test_trackit. We put under functions: the lambda function test_trackit with the handler test.endpoint_test (test is the name of the file and endpoint_test the name of the function in the file). We designed this function to use an http event; the path to execute the lambda will be /test and the request method will be get.

Lambdas in Python

Next, we create the lambda functions in Python which will serve as endpoints of the AWS API Gateway.

To test if the AWS API Gateway is working correctly, we start by creating a test function. To do this, we begin by creating a file called test with a function called endpoint_test which is going to be our handler. If the set up is done correctly the endpoint will return the appropriate JSON file.

class DecimalEncoder(json.JSONEncoder):
 def default(self, o):
 if isinstance(o, decimal.Decimal):
  if o % 1 > 0:
   return float(o)
  else:
   return int(o)
 return super(DecimalEncoder, self).default(o)
def generate_response(status, body, headers={}):
 return {
  “statusCode”: status,
  “body”: json.dumps(body, indent=4, cls=DecimalEncoder),
  “headers”: headers
 }
def endpoint_test(event, context):
 return generate_response(200, {“status”: True})

As you can see in the code, endpoint_test receives two arguments:

  • event
  • context

We are not going to use the specific arguments in this function since they are not within the scope of this tutorial. However, these arguments are very important if you intend to more fully utilize the endpoint functions.

Deployment with Serverless

Once all the previous steps have been completed, you can deploy your first lambda function and API Gateway with Serverless on AWS. This deployment will create an API Gateway with the service name that you’ve chosen (trackit, in this example) and a lambda function in the following format: [service name]-…-[lambda name]

To deploy your configuration, you can execute the following command in the Serverless config directory:


gt; serverless deploy

Once this is done, the next step is to check if the API Gateway works on AWS. The AWS API Gateway Dashboard provides us with the link to the API.

AWS Tools

You need to be connected to your AWS Console for the following steps.

AWS API Gateway Dashboard

To access the API Gateway Dashboard in AWS:

API Gateway → Your API Gateway NAME → Dashboard.

In the API Gateway Dashboard, you will find the link in a blue section at the top that says ‘Invoke this API at [Link]

Logs with Cloudwatch

Where to login with CloudWatch

You can also access Cloudwatch to see the logs of your lambda functions and the logs of the API Gateway as well. You can search for “/aws/lambda/” or “/aws/api-gateway/”. This is an easy way to find your lambda functions and your API Gateway, especially if you have many logs.

Schéma3 Article 2

Lambda in AWS

You can find your lambdas in the AWS Lambda service in the AWS Console.

Schéma4 Article 2

Test endpoints with Postman

Postman allows you to check whether your endpoints work. Once you open Postman, you will have something that looks like this:

Schéma5 Article 2

For this deployment, we have selected GET as the request method. The request URL is in the link that we’ve acquired from the AWS API Gateway Dashboard.

Once you click on the send button Postman will make a request to your API Gateway and execute the lambda function (endpoint). If everything has been set up correctly you will receive a response to the request. (See screenshot below)

Schéma6 Article 2

Amazon Cognito

Amazon Cognito is a service that provides authentication, authorization, and user management for web and mobile applications.

What is Amazon Cognito?

Cognito with Serverless

We can set up Cognito on AWS directly, but it is simpler to create it from the Serverless deployment because we won’t need to get all the Amazon Resource Names (ARNs) for the configuration.

Create a Cognito UserPool with serverless

For this step, open your serverless config file again. First, you need to create a user pool. Under the provider module, create a resources module which contains Resources and a CognitoUserPool

The name we’ve chosen for the user pool in this example is trackittest1 but it’s up to you to choose a name for your user pool. You can also enter and modify additional details like the PasswordPolicy. For instance, if you want the password to have a minimum length of 8 characters, you can change the MinimumLength argument’s value to 8.

Create a Cognito UserPoolClient with serverless

A user pool client is used to connect to the UserPool. Directly below your CognitoUserPool configuration (always under resources), include your CognitoUserPoolClient configuration:

In Properties, you can choose the name of the client and you can link the client to a user pool. Here, we link the client to CognitoUserPool which is the user pool we created in the previous step.

Create an authorization to the AWS API Gateway

Now that we have created our user pool and user pool client, we need to configure the authorization for the AWS API Gateway because certain functions will need an authorized token to be executed.

Under CognitoUserPoolClient you will have to write something like this:

DependsOn relates to the API the authorization will depend on. We enter ApiGatewayRestApi here which is the API automatically created by Serverless when we deploy our Resources. We add IdentitySourcemethod.request.header.Authorization because in the Headers section in Postman the value of the Authorization key needs to be the authorization token. (See screenshot below)

Schéma8 Article 2

Your first endpoint function with authorization

Now you just need to create an endpoint that will require an authorized token. But first, let’s create a user in Cognito.

Create a user in Cognito

You need to deploy Cognito with Serverless with the chosen configuration. This will create a User Pool and a User Pool Client.


gt; serverless deploy

In the AWS Console, go to the Cognito service and click on User Pools. Select the user pool that you have deployed (trackittest1 in this example). Click on ‘Users and groups’ which you will find in the menu on the left.

Schéma9 Article 2

Click on Create user to create a user. (Don’t forget the parameters you’ve selected in the Serverless console such as minimum password length.)

Schéma10 Article 2

Click on Create user when you are done.

We now have to initiate the user and change the password. Go to your terminal and create a file called auth.json (create it in another folder than the one you are deploying with Serverless):


gt; cat auth.json
{
“UserPoolId”: “$user_pool_id”,
“ClientId”: “$client_id”,
“AuthFlow”: “ADMIN_NO_SRP_AUTH”,
“AuthParameters”: {
“USERNAME”: “$username”,
“PASSWORD”: “$password”
}
}

Note that you will have to replace $username and $password variables under AuthParameters based on the user profile you’ve created. In order to find the UserPoolId: in the AWS console go to Cognito → Manage User Pools → Your User Pool Name.

Schéma11 Article 2

To get the App Client Id, click on App Clients in the menu on the left.

You also need to click on Show Details and click on the checkbox: Enable sign-in API for server-based authentication (ADMIN_NO_SRP_AUTH):

Schéma14 Article 2

(If you don’t click the checkbox you won’t be able to make any API requests with a token).

Schéma15 Article 2

You can now execute this command in your terminal (where the auth.json file is located):


gt; aws cognito-idp admin-initiate-auth –cli-input-json file://auth.json

You will have an output like this:

$session” is the key for which you will need to change the password. To change the password, copy “$session” and replace $session with it. (See screenshot below)


gt; aws cognito-idp admin-respond-to-auth-challenge –user-pool-id us-west-2_hrNeH8kvG –client-id 4b1v0nd4n4qenccdk57a7tro2m –challenge-name NEW_PASSWORD_REQUIRED –challenge-responses NEW_PASSWORD=My_pass2,USERNAME=mail1@mail1.com –session $session

Once you’ve done that use this user to test your request to the API. This request will need authorization and you’ll have to execute the following command to get an IdToken:


gt; aws cognito-idp admin-initiate-auth –cli-input-json file://auth.json

Note: Don’t forget to update your password in the auth.json file! If you don’t you will receive an error.

You will have something like this:

Whenever you need to generate an IdToken, you need to execute the previous command. Note that the IdToken is available only for a short period of time.

Create an endpoint which needs authorization with Serverless

Create the function in Python

For this example, we have decided to create our new function in the same file that we’ve used for the other endpoint. Cognito simplifies the process of creating a function that needs an authorized token. All you need to do is create a normal function and edit the Serverless config to deploy the function.

In Python:

class DecimalEncoder(json.JSONEncoder):
 def default(self, o):
 if isinstance(o, decimal.Decimal):
  if o % 1 > 0:
   return float(o)
  else:
   return int(o)
 return super(DecimalEncoder, self).default(o)
def generate_response(status, body, headers={}):
 return {
  “statusCode”: status,
  “body”: json.dumps(body, indent=4, cls=DecimalEncoder),
  “headers”: headers
 }
def endpoint_test(event, context):
 return generate_response(200, {“status”: True})
def endpoint_test_auth(event, context):
 return generate_response(200, {“Hello world”: True})

As you can see, we have created a new function (endpoint_test_auth) that returns a response with the status 200 along with the text “Hello world”. Now we have to edit the serverless config and add the new endpoint:

The process is quite similar to what we did with the other endpoint. The only new thing here is we need to add an authorizer so the API Gateway knows that there’s an authorized token.

The authorizerId is in ApiGatewayAuthorizer:

You can now deploy with:


gt; serverless deploy

Go to the API Gateway in your AWS Console and you’ll find both the functions you created in the Resources section:

Schéma14 Article 2

Test your endpoint which needs authorization with Postman

On Postman, as we did before, you’ll have to put your request URL using the correct request method as seen in the screenshot below:

Under ‘Key’, assign a name (‘Authorization’, for instance) to your key and under ‘Value’, enter your new IdToken which you generated with auth.json. Then click on send.

Schéma15 Article 2

Schéma16 Article 2

The successful output will be the following:

Note: You should attempt it without the authorization values in the headers and make a request. If the authorization has been set up correctly, the response to the request will be an error:

How much does it cost?

With this specific AWS API Gateway, the cost for 1,000,000 “Hello world”s is $3.50. It’s important to note that it was not an easy task to exceed the free AWS plan to end up with this cost!

Schéma17 Article 2

An Indispensable, Easy-To-Deploy, and Cost-Effective Solution

AWS API Gateways are not only practically universal in terms of their applicability to different infrastructure workflows but also quite hard to do without. AWS API Gateways apply to anyone who wants to create a modern software stack that’s built to seamlessly accommodate future modifications and integrations.

One of the primary appeals of AWS API Gateways is the ease with which one can deploy them. The creation of the API presented in this article should take no more than an hour. Most of the heavy lifting is done by AWS and the only coding knowledge necessary is that required for the creation of relatively simple lambda functions.

In order to reproduce the features of an API in the absence of an API Gateway like the one we’ve presented in this article, one would have to set up an EC2 instance and create an API in Python with a common framework like Flask. If you think this a daunting task, wait till you reach the point where you have to come up with an alternative for Cognito!

Even for a quick project like this one, it would take at least a few hours to set up Flask. Despite such efforts the overall cost of the solution would become an issue due to the maintenance costs and SysOps efforts that go hand in hand with EC2 instances. Deploying an AWS API Gateway like the one we’ve discussed in this article is not just simple and efficient, but also cost-effective. The reason for this is twofold: first, with AWS you only pay for what you use; and second, you practically eliminate all human costs by implementing an AWS API Gateway using the steps outlined in this article.

About TrackIt

TrackIt is an Amazon Web Services Advanced Consulting Partner specializing in cloud management, consulting and software development solutions based in Venice, CA.

We specialize in Modern Software Development; DevOps, Infrastructure-As-Code, Serverless, CI/CD and Containerization with specialized expertise in Media & Entertainment workflows, High-Performance Computing environments and data storage.

Here is the GitHub repository of the tutorial: https://github.com/trackit/aws-api-gateway-cognito