# Securing API with Cognito User Pool

Hello Everyone,

In this blog, we’ll explore Amazon Cognito, a powerful AWS service for managing user authentication and access control in applications. We’ll also demonstrate how to use Amazon Cognito to secure APIs, ensuring that only authorized users can access our resources.

---

In this demo, we will secure an API deployed through API Gateway. By default, API Gateway does not provide authorization or security, which means anyone can access our endpoint.

We’ll use a Cognito Identity Pool to create users and configure an API that triggers a Lambda function. After successful sign-in, users will receive a token. API Gateway will validate this token to grant access to the request.

Additionally, we’ll use Postman to test our API. Postman allows us to send requests, configure details like headers, and view responses. It helps ensure that our API is properly secured and functioning as expected.

---

### Task 1: Creating Lambda function

First, let's set up our backend server, lambda.

Search for **lambda** in the search box.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723163984958/8a2f224c-5289-44ce-8574-18cdefc20832.png align="left")

Click on `Create a function`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164028826/00e39c6d-ea32-4fa0-9ff9-36f0a15ec846.png align="left")

Provide name for your function and select **Python** as runtime.

Keeping all other configurations as is, click on `Create function`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164159114/6287d816-2b0c-475a-aef8-7690951f133b.png align="left")

We are just returning a simple hello message from lambda.

Click on `Deploy`.

Now, click on `Test`. To test our function, we have to provide a test event, for now, just provide the name for test event and click on `Save`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164282773/853addca-c3f4-47e0-ad79-a917083f4d8a.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164405565/7426055a-7904-4ddc-82d7-d35b832990f2.png align="left")

Now, click on **Test** to see if the function is working as expected. We are getting the hello message.

Alright, that's the first job done.

---

### Task 2: Creating an API

We will be creating a simple REST GET API.

Search for **API Gateway** in the search box.

Scroll down to **REST API**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164561234/e65320a7-752d-4f70-b327-2338a5ded375.png align="left")

Click on `Build`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164613047/18441c34-7bb8-4e80-83b5-afa268183987.png align="left")

Provide a name for your API and click on `Create API`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164679219/bd3ccf4b-fd4d-4d38-97dd-e5ebea0f4c24.png align="left")

Now we are at the root (`/`) level.

Let's create a method. Click on `Create Method`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723164755542/ccc2ca9f-26e7-4dbf-b7c2-277120e12f2a.png align="left")

Configure Method type as **GET**, Integration type as **Lambda function** and select your created lambda function.

Click on `Create method`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723170129323/ee7f827a-dea8-402b-9f79-97305462fffc.png align="center")

Click on `Deploy API`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723170162235/1dc22d49-4791-4cb8-9b9b-cd38c05b87f3.png align="left")

Choose New stage and provide a suitable Stage name.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723170206356/488a42ce-1f95-4851-a6ca-ea59078b425f.png align="left")

Select the GET method and copy the invoke URL from your stage section.

Let's head over to the Postman.

[![Icon for package postman](https://imgs.search.brave.com/6aVW64lywy3pCZ92XZZWT4x2dCJZAfThpPqiODIo7gI/rs:fit:500:0:0:0/g:ce/aHR0cHM6Ly9jb21t/dW5pdHkuY2hvY29s/YXRleS5vcmcvY29u/dGVudC9wYWNrYWdl/aW1hZ2VzL3Bvc3Rt/YW4uMTEuMS4wLnBu/Zw align="center")](https://www.postman.com/)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723170309809/1ed33646-d1b2-4100-9f2a-106f9962684a.png align="center")

Select **GET** method in Postman, paste the copied URL and click on `Send`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723170390248/912c3c12-3550-4433-87fb-4365dd1eb85c.png align="center")

We get the response from our lambda function.

***"Hello from Lambda"***

---

### Task 3: Creating User Pool

Now, let's create a User Pool.

A **User Pool** is a user directory in Amazon Cognito that helps manage and authenticate users. When users sign up or sign in to your application, they interact with the user pool.

Search for **Amazon Cognito** in the search box.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723108871872/f856a8fd-52f1-432a-ab50-df063fc81679.png align="left")

Click on `Create user pool`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723108944572/c2ed76fc-eec8-48aa-ad0b-2c34f26991d8.png align="center")

For sign-in options, select **Email**, then click on `Next`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723110536606/5d8010d4-1823-4828-a339-8c64f1b55167.png align="left")

For password policy, we can go with defaults, but I made some changes to make easier requirements for password in the Custom mode.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723109851599/bd7e0dec-1506-40f2-8199-66b2a8a4ca54.png align="left")

We decided to go for **No MFA** this time.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723109952288/6a3acd70-554c-4d8b-9770-bdb2297d6631.png align="center")

Leave the **Self-service account recovery** option as default.

This option configures how users will recover their account when they forget their password.

Click on `Next`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723110753096/a49476b3-293e-4437-b6df-5ae48604a990.png align="center")

Keep the **Self-registration** option as is. This allows new users to register for an account themselves.

Scroll down to bottom, leaving all other configurations as default.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723111188943/03346be7-a230-4d74-b5c7-0cb8e4f684fe.png align="left")

We can choose the attributes that are required when a new user is created.

I added name as additional required attributes. You can select other attributes to add as you like.

Click on `Next`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723111909733/6094abea-6ff1-4fd8-b37c-e1a4f0fd701b.png align="center")

Select the **Send email with Cognito** option. The **FROM** email address shows who sent the email, while the **REPLY-TO** address indicates where replies will be sent, which can be different from the **FROM** address.

Click on `Next`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723112807373/ce00e2f5-aa1f-4fad-b32e-1fa0d37ddd47.png align="left")

Provide a suitable name for your User pool.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723112850310/c24fb718-8955-446b-8ee4-d8810c7d91b1.png align="left")

Tick the **Use the Cognito Hosted UI** box. This allows us to use Amazon Cognito's built-in user interface for authentication and sign-up, simplifying the process of integrating user sign-in and sign-up into your application.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723128126723/3e95a815-3ea9-4b9c-a357-1ff8f071501b.png align="left")

Select the Cognito domain. We can use our custom domain, which involves configuring a custom domain name for the Cognito Hosted UI.

Essentially, this is the domain through which users will access the authentication interface for signing in or signing up.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723115682720/390f38d3-a493-442b-8b4b-4acfd92e90a0.png align="left")

Provide a suitable **App client name**. An initial app client in Amazon Cognito is essentially an identity for your application to interact with a user pool.

<details data-node-type="hn-details-summary"><summary>App Client</summary><div data-type="detailsContent">An app client in Amazon Cognito represents a specific application's configuration for interacting with a user pool, including settings like OAuth flows, callback URLs, and authentication methods. The term "client" reflects the application's role in consuming the authentication service. Multiple app clients can be linked to the same user pool, each tailored for different applications, allowing them to use the same user credentials while maintaining distinct configurations.</div></details>![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723275223772/1f8674c4-d107-42c8-b18e-c6a9ea611c74.png align="left")

The client secret helps ensure that the client making requests to the Cognito service is indeed the one that was registered.

We are not generating for now.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723113784501/0c190339-33e6-4f51-a931-4416aa65c81d.png align="left")

We are using [`example.com`](http://example.com) as our callback URL for demo purpose.

Callback URLs are the addresses where users are sent after they log in or complete an authentication process. They direct users back to your app with the authentication details.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723128021687/7c8173a8-c080-4d9e-89b9-4b6d25ba29d6.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723132390036/33eb912d-6ee8-4950-b43e-748a7203eb06.png align="center")

In the **Advanced client app** settings, we can customize the values as required.

Scroll down to the **OAuth 2.0 grant types** section.

OAuth 2.0 grant types are methods for obtaining access tokens to access protected resources.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723134627879/68c9ec11-d1eb-4207-902b-b33d9267d108.png align="center")

Let's explore the workflows briefly.

**Authorization Code Grant**

* The user logs in via the chosen authorization server (e.g., Amazon Cognito, Google Identity, or another OAuth 2.0 provider).
    
* Authorization Server redirects back with an authorization code.
    
* Your backend exchanges the code for tokens.
    
* Use the tokens to access protected resources
    

**Implicit Grant**

1. User logs in via the chosen authorization server.
    
2. Authorization server redirects back with tokens in the URL.
    
3. Use the tokens directly to access resources.
    

We are using **Implicit Grant** to obtain an access token directly from the authorization server.

Keeping all other configurations as default, click on `Next`.

Review your selections and click on `Create user pool`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723134873692/617b3c96-6089-4546-92a6-3440d57981b8.png align="left")

Our User pool has been created. Click on your created user pool.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723160844079/501b83b4-fb45-4a50-a095-24f0bf51fd6a.png align="left")

Let's create a user in the Users section.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723160950773/31057486-2280-4358-9644-127de41b24c5.png align="left")

Provide email address and set password, then click on `Create user`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723161158900/b00bf3f1-de9b-41cf-8dfb-aee1040d76d6.png align="center")

A user has been created.

---

### Task 4: Creating & testing API Gateway Authorizer

It's time to add **Authorizers** in API Gateway that will authenticate and authorize requests to our API. This ensures that only users with valid credentials can access our API resources.

Let's go to the API Gateway.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723165014222/2c967172-407a-4cd7-8ba0-a30058829f18.png align="center")

In the left panel, in **Authorizers**, click on `Create an authorizer`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723166191827/0aa6c08e-cfa6-43e9-b02c-f3d7c4b17967.png align="center")

Provide a suitable name, select **Cognito** as Authorizer type, select your user pool and provide the header as **Authorization** in the **Token source**.

Click on `Create authorizer`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723284370385/5b734f79-0df2-4328-91b3-19ea3e29bd87.png align="center")

---

### Task 5: Signing in and Testing API Gateway Authorizer

To test the authorizer, we need a token value, which is obtained after a successful sign-in. Therefore, let's proceed with the sign-in process.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723279373072/3a7255e6-d959-4ce0-a53f-acabede29bd7.png align="center")

To view the sign-in page, we have two approaches: a direct link and a customized URL method. The direct link is simpler for basic use cases, while the customized URL method offers flexibility and customization for more complex scenarios and specific needs.

[Click to get the URL](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-app-integration.html)

Let's implement the customized URL approach first.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723161389070/8b349f3d-03d4-424a-a882-55e8be977382.png align="left")

There are two different URLs given, one for **authorization code grant** and another for **implicit code grant**.

We are using the second URL.

```xml

https://your_domain/login?response_type=token&client_id=your_app_client_id&redirect_uri=your_callback_url
```

We need to replace ***your\_domain, your\_app\_client\_id*** & ***your\_callback\_url*** fields with the original values.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723161718428/3d4a83f3-e1a4-402c-b718-983adb6ed54d.png align="left")

In the **App integration** tab, you'll find your domain link in the **Domain** section.

Copy and replace it with ***your\_domain*** in the URL.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723161871867/9e9831c3-8eae-4897-8e9c-0cfa6a3c5e83.png align="center")

In the same section, scroll to the bottom, and we'll find our App client list. Click on your App client.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723161928787/961503de-f090-4fb0-a467-9c6a32b9d0cc.png align="center")

Copy your **Client ID** and replace with ***your\_app\_client\_id*** in the URL.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723162336584/f0495690-c48e-4124-b137-d73db16981b0.png align="left")

Scroll down a bit, and you'll find your allowed callback URLs that we had provided earlier.

Copy that and paste it into the above URL in ***your\_callback\_URL***.

```bash
#My final URL
https://demodomain1.auth.ap-south-1.amazoncognito.com/login?response_type=token&client_id=6pv54rin409v9ngddqb932lade&redirect_uri=https://example.com
```

Copy and paste the complete URL into a new browser tab to access the sign-in page.

Alternatively, we can use a direct link for a more straightforward approach.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723162812929/c35fe1b4-507e-48cb-9fba-773c5808c4ca.png align="center")

In your **App client**, in **Hosted UI** section, in the top right corner, we can see **View Hosted UI**.

Click on that button.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723163077453/a643984e-0b7e-45c5-b50c-b36cceaac2a8.png align="center")

Sign in with your configured email & password.

We can also create new users using the **Sign up** link, as we have enabled Self-service sign-up option.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723163166649/d11b0131-9866-4f94-b32d-2b7e95132bbd.png align="center")

Since this is my first time signing in, I need to set up a new password. Additionally, because I set the name as a required attribute, it also appears on this page.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723163326998/4f57c1b6-7bbb-4899-8e8a-530da2f7036c.png align="center")

Click on `Send`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723163392126/5d7bf7a3-ef47-479e-9bda-15117f97a9ca.png align="left")

Our sign-in was a success, and we're directed to the callback URL that we had set up.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723198632136/b48320e6-f87d-4a8a-a502-85a7a3052686.png align="left")

Notice in the URL, there's a token provided by Cognito. We'll copy this token to test our Authorizer.

Copy the token and paste it into your text editor.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723198706827/f129d9e8-b476-44da-a328-d37f42370f07.png align="center")

Only copy the **#id\_token** value from the URL.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723198754111/5b7d399d-6780-4fd5-973c-b3bd3c16c9cb.png align="center")

Paste the copied token in the Token value section and click on `Test authorizer`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723199035859/0077cb61-39f7-41f7-adf9-85a8a0bd8d78.png align="left")

We got this output, and this is what's known as the **decoded JWT claims,** or just **JWT claims**. It's the data returned by the API Gateway authorizer during testing, representing various details about the user contained within the token used for authorization.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723200803784/7b917f7c-7f7c-4033-9dc1-771a9a7a4046.png align="left")

We can pass an invalid token value to observe the response we receive.

---

### Task 6: Adding Authorizer to GET Method

Now in our **GET** method, we have to add the authorization in the method request.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723166435030/c50ae45b-e451-4212-8c10-aae2a0867380.png align="left")

Click on **Method request** & scroll a bit to see the body section.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723166473778/094d2706-0812-49d5-a46e-39cca7a52835.png align="left")

Click on `Edit`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723166510534/0e5ca2d2-5a4e-4077-ad2c-ebd1235022ef.png align="left")

Select your authorizer and click on `Save`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723171294388/95e55060-2b7d-4377-b0e2-0a4383100352.png align="center")

After making the change, redeploy the API by clicking on `Deploy API`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723171335373/a39fcc36-9420-4416-8030-bfb12c2c9300.png align="center")

After deploying the API, copy the URL and open Postman to test the **GET** request.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723171402500/cdeb6127-96ff-4d3f-a79d-150d8e94b439.png align="center")

Paste the URL then click on `Send`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723171432249/f220a250-0cd4-4a52-bc8b-220cdba500dd.png align="center")

This time, we see an **“Unauthorized”** error.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723173179047/dcb72794-a9a7-45e1-a866-251991f719b4.png align="center")

In API Gateway, the authorizer is configured to expect the token in a header field called **Authorization**.

Let's copy our token again.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723198706827/f129d9e8-b476-44da-a328-d37f42370f07.png align="left")

In the Headers section, add **Authorization** as the key and paste the token as the value. Then, click on `Send`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723173244840/3963e1c6-4a6b-43f7-b06b-008d76cb2d5d.png align="center")

Now that we have provided the token, the authorizer has validated it and granted access to the backend.

*Note that if your token is not working, it may have expired. In this case, you will need to sign in again to obtain a new token.*

---

### Task 7: Conclusion

In this blog, we secured an API with Amazon Cognito and API Gateway. We set up a Lambda function, deployed a REST API, configured a Cognito User Pool, and used API Gateway Authorizers. Finally, we tested the setup with Postman.

---

### Task 8: Clean Up

1. **Deleting API.**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723282518936/13932348-6e90-4ddc-a58e-e229458306b9.png align="left")
    
    Select your API and click on Delete to delete your API.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723282582649/3a5ef08d-5ab6-4280-9379-5c586aabd870.png align="left")
    
2. **Deleting Lambda function.**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723282640203/7460a2bd-81c1-4be4-8763-383e7b29d755.png align="center")
    
    Select your lambda function and in the **Actions** drop-down menu, click on `Delete`.
    
3. **Deleting User Pool.**
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723282758459/d668699a-b468-46a3-9651-c2201d04f7de.png align="left")
    
    Select your User pool and click on `Delete`.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723283109802/c650c99d-c902-44dc-ba7f-da8139d48589.png align="left")
    

---

Alright, that sums up our today's work.

See you later in the next one!
