Securing APIs with AWS Amplify and Cognito
Tags: restapi, amplify, cognito, aws
Published: 2021-01-06 09:37:36

# Securing APIs with AWS Amplify and Cognito

[![We are hiring!](https://dev-to-uploads.s3.amazonaws.com/i/fnbppwjezekl7t56nfxz.png)](https://epilot.cloud/en/career-engineering/)

## Overview

AWS Amplify is one of the fastest ways to help front-end web and mobile developers build full stack applications, hosted in AWS.

In the [first part](https://cloudychetan.medium.com/using-amplify-for-rest-apis-and-web-hosting-f27ad2867908) of this blog series, [Using Amplify for REST APIs and Web hosting](https://cloudychetan.medium.com/using-amplify-for-rest-apis-and-web-hosting-f27ad2867908) we built an API using AWS Amplify to quickly setup and host an API with minimal coding.

In this part we build an authentication system for the API using [Amazon Cognito](https://aws.amazon.com/cognito/).

Amazon Cognito lets us add user sign-up, sign-in, and access control to our web and mobile apps quickly and easily. It is highly scaleable and supports sign-in with social identity providers, such as Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0.

## Advantages

* Secure and scalable user directory

* Standards-based authentication

* Security for your apps and users

* Easy integration with our App


## What to Build ?

In this Blog we quickly build an authentication system for a simple REST API using AWS Amplify CLI.

We will build

* An authenticated ExpressJS server that returns a random number

## Pre-requisites

* Amplify CLI — Open terminal and run npm install -g @aws-amplify/clito update to the latest Amplify CLI.

* Amplify CLI configured — If you have not configured the Amplify CLI yet, follow [this guide](https://docs.amplify.aws/cli/start/install#configure-the-amplify-cli) in the documentation.

## Setup a new Amplify project

* Run the following command to create a new Amplify project called “amplify-rest-containerized” or if you already have an existing Amplify project skip to the next section.

```    
mkdir amplify-rest-containerized
cd amplify-rest-containerized
```

* Initialize an Amplify project by running:

```
    amplify init
```

For a quick implementation, we can just accept the default values in the amplify init workflow console.

## Enable container based deployments

* Container-based deployments need to be explicitly enabled. Run **amplify configure project** to review your project configuration:

```
    amplify configure project
```

* Accept the defaults and answer **Yes** when asked if you want to enable container-based deployments:

```
    ...
    ? Do you want to enable container-based deployments? Yes
```

## Add an authenticated container-based ExpressJS API

For the demo, let’s quickly create a REST API. More detailed explanation can be found [here](https://cloudychetan.medium.com/using-amplify-for-rest-apis-and-web-hosting-f27ad2867908)

* To create our authenticated container-based REST API, execute:

```
    amplify add api
```

* Choose the following options:

```
? Please select from one of the below mentioned services: REST
    ? Which service would you like to use API Gateway + AWS Fargate (Container-based)
    ? Provide a friendly name for your resource to be used as a label for this category in the project: container622e561f
    ? What image would you like to use ExpressJS - REST template
    ? When do you want to build & deploy the Fargate task On every "amplify push" (Fully managed container source)
    ? Do you want to restrict API access Yes
    Successfully added auth resource locally.
    Successfully added resource container622e561f locally.
```

* After a successful completion of the CLI workflow, we’ll see these new files added to the project folder structure.

```
    amplify/backend/api/<your-api-name>
    ├── amplify.state
    ├── containerb5734e35-cloudformation-template.json
    ├── parameters.json
    └── src
        ├── Dockerfile
        ├── DynamoDBActions.js
        ├── buildspec.yml
        ├── index.js
        ├── package-lock.json
        └── package.json

    amplify/backend/auth/<your-api-name>
    ├── cognitod17bb34c-cloudformation-template.yml
    └── parameters.json
```

* In the **src/index.js** we will find a starter ExpressJS source code to interact with DynamoDB.
Let’s edit that to return a random number.

* Replace the **index.js** file with the following code:

```
    const express = require("express");
    const bodyParser = require('body-parser');
    const port = process.env.PORT || 3001;const app = express();
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));// Enable CORS for all methods
    app.use(function (req, res, next) {
        res.header("Access-Control-Allow-Origin", "*")
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
        next()
    });app.get("/", async (req, res, next) => {    try {
            res.contentType("application/json").send({ 
                "randomNumber": Math.floor(Math.random() * 101) 
            })
        } catch (err) {
            next(err);
        }
    });app.listen(port, () => {
        console.log('Example app listening at [http://localhost:'](http://localhost:') + port);
    });
```

## Deploy

* Now let’s deploy our API:

```
    amplify push
    ✔ Successfully pulled backend environment dev from the cloud.

    Current Environment: dev

    | Category | Resource name     | Operation | Provider plugin   |
    | -------- | ----------------- | --------- | ----------------- |
    | Auth     | cognitod17bb34c   | Create    | awscloudformation |
    | Api      | container622e561f | Create    | awscloudformation |
    ? Are you sure you want to continue? Yes
```

* As we see amplify also deploys authentication related resources.

## What’s happening under the hood ?

* We are deploying an API just like the [first blog](https://cloudychetan.medium.com/using-amplify-for-rest-apis-and-web-hosting-f27ad2867908)

* Adding authentication related resources like AWS Cognito User Pool, Identity Pool, User Pool Client, Client Role, Authorizer Lambda function

* Amplify uses a [Nested CloudFormation Stack](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html) to deploy each component from the App. In this example, it would deploy a network stack, a cognito resources stack and a containerized API stack.

## Test our containerized and authenticated ExpressJS API

This is where we come to the tricky part.

There are several ways in which we can interact with AWS Cognito APIs to get the necessary authentication token for getting access to the API.

* [**AWS SDK for JavaScript**](https://docs.amplify.aws/lib/auth/getting-started/q/platform/js#create-authentication-service) to include components in your code to obtain token using pre-built UI components in React.

* **AWS CLI** commands to perform sign-up for a user, confirm the user registration and request for generating a token for authentication.

For quick testing, we would use the **AWS CLI** generated token for authenticating our request to the API.

### Pre-requisites to generate Token

You would need the following information before you start the process

* The **AppClient-ID** for the AWS Cognito User Pool — This can be obtained from AWS Console for AWS Cognito or from the CloudFormation Stack Resources Details Tab

* The **UserPool-ID** for the AWS Cognito User Pool — This can be obtained from AWS Console for AWS Cognito or from the CloudFormation Stack Resources Details Tab

### How to generate the token ?

* Create a Sign-up request for the required User

```
    aws cognito-idp sign-up --region <region> --client-id <client_id> --username xyz.abc --password xxxx --user-
    attributes Name="email",Value="xyz.abc@efg.com"

    Response :
    {
        "UserConfirmed": false,
        "CodeDeliveryDetails": {
            "Destination": "xxx@xxx.com",
            "DeliveryMedium": "EMAIL",
            "AttributeName": "email"
        },
        "UserSub": "0bf218e6-65ab-4160-8ae9-48bb4e9f22cd"
    }
```

* Confirm the registration of the User

```
    aws cognito-idp admin-confirm-sign-up --region <region> --user-pool-id <user_pool_id> --username xyz.abc
```

* The password set for the new user in Step 1 is temporary and it needs to be changed before generating the authentication request
```
    aws cognito-idp initiate-auth \
            --client-id <client_id> \
            --auth-flow USER_PASSWORD_AUTH \
            --auth-parameters "USERNAME=xyz.abc,PASSWORD=xxxxxxxx"

    Response:
    
    {
        "ChallengeName": 'NEW_PASSWORD_REQUIRED',
        'ClientId' => '<client_id>',
        'Session' => <session_token>,
    }
```

* Use the **session_token** from the above response to set the new password

```
    aws cognito-idp respond-to-auth-challenge --client-id <client_id> --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses USERNAME=xyz.abc,NEW_PASSWORD="xxxxxxxx" --session "<session_token>"
```

* The above request responds with the Tokens generated after successful authentication with AWS Cognito

```
    {
        "ChallengeParameters": {},
        "AuthenticationResult": {
            "AccessToken": "xxxxxxxxxxxxxxxxxxx",
            "ExpiresIn": 3600,
            "TokenType": "Bearer",
            "RefreshToken": "xxxxxxxxxxxxxxxxxxx",
            "IdToken": "xxxxxxxxxxxxxxxxxxx"
        }
    }
```

* Finally we can use the **AccessToken** to test our API

### Testing the API

The best way to demonstrate the authenticated API is from the Postman App using Authentication.

* Setup authentication parameters in Postman App — Enter the AccessToken in the Token section.

![](https://cdn-images-1.medium.com/max/5128/1*rpl7-n0RCiZsSCEX27XS8g.png)

* Execute the request to the API

![](https://cdn-images-1.medium.com/max/5128/1*mnzHgZY_oD7UPvQsAaQmbA.png)

The API endpoint is printed at the end of the “**amplify push**” command or when you execute “**amplify status**”.

**Note**: This is a simple use case just to showcase the workflow. 
We used the AWS CLI to quickly demonstrate the authentication flow. However, actual authentication for your app should be done seamlessly using the appropriate AWS SDK for your code.
Review Amplify documentation if you’re interested in any other scenario.

## What did we learn ?

This blog post gives a quick way to deploy an authenticated API using Amplify CLI.

* Quickly setup and authenticated API using AWS Amplify

* How to authenticate the requests to the authenticated API using AWS CLI

There is so much more to explore.

Keep building…

[![We are hiring!](https://dev-to-uploads.s3.amazonaws.com/i/fnbppwjezekl7t56nfxz.png)](https://epilot.cloud/en/career-engineering/)