User control with Cognito and API Gateway

Carlos Hernández
carlos-hernandez
Published in
5 min readMay 17, 2021

--

In this article, I am going to explain how we can take advantage of Cognito and API Gateway to manage user access control in our application quickly and simply.

Cognito provides a service that can scale to millions of users and supports sign-in with a variety of third-party identity providers. But more importantly we do not have to worry about security issues or bugs. Cognito supports identity and access management standards, such as Oauth 2.0, SAML 2.0, and OpenID Connect.

Below we can see a diagram of our system. In this case, we are going to simulate that a client wants to check-in at our Royal Hotel.

All of the examples included in this article will be written using IaC(Infrastructure as Code) in particular Terraform. At the end of this article you will find a link to the code.

Cognito

The first step is to create our user pool in Cognito (royal-pool-user). We are going to save the following information from our client:

  • Username: This field will be case insensitive.
  • Password: It should have a minimum of 6 characters and it should contain at least one uppercase, lowercase, number and symbol.
  • Email: It should have a length between 7 and 256 characters and it will be auto-validated by Cognito.
  • Name: It should have a length between 3 and 256 characters.

The property alias_attibute = [“email”] means that our user can use indistinctly the username or email to identify themselves on the login.

Now that we have our pool to save the user information we need to create a Cognito pool client. This will give us access to our pool through an API.

After creating our pool client it is time to test it. Let’s create our first client using the AWS CLI:

In our case, we are requesting to validate the email to confirm the user. We can use the command below to auto-confirm our user. This will not validate the email but it will activate the user which will be enough to validate our implementation:

We can get the client-id and user-pool-id as the output of our terraform script which you can find in this repository.

Finally, we can check that our user was created successfully and its UserStatus is CONFIRMED using the following command:

API GATEWAY

At this point, we are ready to go one step forward and it is time to create our API. In this section, we are going to create an end-point that returns a welcome message with the client name.

First, we need to create our API Gateway, called Royal API:

Inside our Royal API, we are going to create the check-in resource, this will be our end-point. This resource depends on the API Gateway previously created which means we have to pass it as parameters (id and root_resource_id):

The previous code will create our API and path as we can see in the picture below:

Now that we have our path defined we can create an HTTP method. In our particular case (Lambda integration) this method is composed of 3 elements.

API Method

This resource defines the HTTP method and the type of authentication that we are going to use for this end-point. In our case, this will be GET and Cognito respective. We will cover how to define a Cognito’s authorizer later on in this article.

API Integration

The integration defines how the API is connected to an external resource, in this case, a Lambda. This integration is through a POST method using the type AWS_PROXY, as we can see below:

Method Response

To define this resource, we are using a for loop so as not to repeat code. Normally we would define more than one status code as a response, for example, 200 (Ok) and 500 (Internal server error). The statement for_each will create the same resource for each value in the array api_status_response:

This will be the result:

Cognito Authorizer

Integrating Cognito with our API is simple, we just need to create an authorizer of type COGNITO_USER_POOLS and pass an array of the Amazon Cognito user pool ARNs.

Lambda

Finally, we need some code to create the welcome message with the client’s name. To execute this code we will use a Lambda with the particularity that we are going to combine it with a Lambda Layer:

Lambda Layers are a good practice to reuse code between different Lambdas instead of duplicating code. We can have a max of 5 Layers by Lambda. In our example, we need to use an external library to decode the JWT from Cognito to get the client’s name.

It is important to define our library under a specific path as we can see in the official documentation. In our case, we are using Node.js so we have to define them under nodejs/node_modules.

Once we have defined the Lambda Layer it is time to create the Lamda function. First, we need to create a role, attach an AWSLambdaBasicExecutionRole role policy to it and grant permission to execute it from the AWS API Gateway:

After creating the role and giving it the correct permissions we can define the Lambda resource in our Terraform script:

As we mentioned before in our lambda code we are just going to decode the JSON Web Token using an external library and return a personal welcome message using the client’s name as we can see here:

Testing our Implementation

Well done! We have created our end-point in API Gateway and secured it using Cognito. So now we have to test it, and check that it is working as expected. In this section, we are going to use CURL to test it. The first step is to get our user token using the following command:

Finally, we execute the CURL command using the token gotten previously and we should receive our welcome message:

Summary

In this article, we created an API and secured it using Cognito. As you can see, Cognito simplifies the way in which we can securely manage users in our application, saving us time and money.

The next step is to integrate Cognito with our front-end application using Amplify. Let me know if you would like to know more about this and I will happily write an article on it.

Note: you can find the previous code in this repository.

--

--