How to add Amazon Cognito Auth to a Web App (part 2)
Tags: amazoncognito, amazonwebservices, devops, terraform
Published: 2023-02-05 13:28:09

---
title: How to add Amazon Cognito Auth to a Web App (part 2)
published: true
date: 2023-02-05 12:58:24 UTC
tags: amazoncognito,amazonwebservices,devops,terraform
canonical_url: https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-2/
---

Terraform code samples that demonstrate how to set up Amazon Cognito Auth in a Web App.

![](https://cdn-images-1.medium.com/max/1024/1*7k3pnvN8jO5epshfJwXcGQ.jpeg)

### Intro

In the [last blog post](https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/), I talked through the concepts of how Amazon Cognito works in a Web App.

In this post, I will explain how Amazon Cognito can be programmatically set up using Terraform — so you can build cool stuff like this 👇

![](https://cdn-images-1.medium.com/max/600/1*ZwnJBv07kegP57Pn0q7ysg.gif)

### Creating Amazon Cognito with Terraform​

I have a [Wrapper.js template](https://jamesmiller.blog/wrapperjs-auth-template/) that you can use to spin up an implementation of Cognito, here are some highlight files from that template:

I use Terraform as the tool to create the majority of the cloud infrastructure.

#### main.tf​

This is the core file that creates all the terraform resources, I’ll go deeper into lines 7–12 and 42–52, which are used to create Cognito and the Secrets Manager that is used to pass the Cognito details to both the Back End and Front End.

```
module "nextjs_app_s3" {
    source = "./modules/s3"
    bucket = var.domain_name
    acl = "public-read"
}

module "acm" {
    source = "./modules/acm"
    root_domain_name = var.root_domain_name
    domain_name = var.domain_name
    region = "us-east-1"
}

module "nextjs_app_cloudfront" {
    source = "./modules/cloudfront"
    hosted_zone_name = var.root_domain_name
    domain_name = var.domain_name
    # lambda_edge_qualified_arn = module.uri_redirect_edge_lambda.qualified_arn
    acm_cert_arn = module.acm.cert_arn
    acm_cert_id = module.acm.cert_id
    s3_website_endpoint = module.nextjs_app_s3.website_endpoint
    default_root_object = "index.html"
}

module "rest_api_gateway" {
    source = "./modules/restApiGateway"
    certificate_arn = module.acm.cert_arn
    root_domain_name = var.root_domain_name
    domain_name = "api.${var.domain_name}"
    certificate_id = module.acm.cert_id
    stage_name = var.stage
    cognito_arn = module.cognito.user_pool_client_arn
}

module "cognito" {
    source = "./modules/cognito"
    domain_name = var.domain_name
    stage_name = var.stage
    service_name = var.service_name
}

module "secrets_manager" {
    source = "./modules/secretsManager"
    service_name = var.service_name
    cognito_identity_pool_id = module.cognito.identity_pool_id
    cognito_user_pool_id = module.cognito.user_pool_id
    cognito_user_pool_client_id = module.cognito.user_pool_client_id
    cognito_user_pool_client_arn = module.cognito.user_pool_client_arn
    cognito_authorizer = module.rest_api_gateway.cognito_authorizer
    api_gateway_rest_api_id = module.rest_api_gateway.rest_api_id
    api_gateway_root_resource_id = module.rest_api_gateway.root_resource_id
}
```

#### modules/cognito/main.tf​

This is the file that creates the Amazon Cognito configuration.

Here you can see how the User Pool (lines 1–3) and Identity Pool (lines 11–20) are configured to allow authenticated users access to aws resources.

```
resource "aws_cognito_user_pool" "this" {
  name = "${var.service_name}-${var.stage_name}-pool"
}

resource "aws_cognito_user_pool_client" "this" {
  name = "${var.service_name}-${var.stage_name}-client"

  user_pool_id = aws_cognito_user_pool.this.id
}

resource "aws_cognito_identity_pool" "this" {
  identity_pool_name = "${var.service_name}_${var.stage_name}_identity_pool"
  allow_unauthenticated_identities = false

  cognito_identity_providers {
    client_id = aws_cognito_user_pool_client.this.id
    provider_name = "cognito-idp.eu-west-2.amazonaws.com/${aws_cognito_user_pool.this.id}"
    server_side_token_check = false
  }
}

resource "aws_iam_role" "authenticated" {
  name = "${var.service_name}-${var.stage_name}-cognito_authenticated"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "${aws_cognito_identity_pool.this.id}"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "authenticated" {
  name = "${var.service_name}-${var.stage_name}-authenticated_policy"
  role = aws_iam_role.authenticated.id

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*",
        "cognito-identity:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}

resource "aws_cognito_identity_pool_roles_attachment" "this" {
  identity_pool_id = aws_cognito_identity_pool.this.id

  roles = {
    authenticated = aws_iam_role.authenticated.arn
  }
}
```

#### modules/cognito/outputs.tf

After creating your Amazon Cognito configuration, the outputs file allows you to export variables to be used in other parts of the Terraform configuration.

This outputs file generates 4 variables that will enable the Front End and Back End to interact with Amazon Cognito to generate and use JSON Web Tokens (JWT’s).

```
output "identity_pool_id" {
  value = aws_cognito_identity_pool.this.id
}

output "user_pool_id" {
  value = aws_cognito_user_pool.this.id
}

output "user_pool_client_id" {
  value = aws_cognito_user_pool_client.this.id
}

output "user_pool_client_arn" {
  value = aws_cognito_user_pool.this.arn
}
```

#### modules/restApiGateway/authorizer.tf

One of the important resources that will allow the Back End to leverage Cognito Auth is an API Gateway Authorizer.

Here you can see one the 4 variables, the user pool arn — that is used to create an authorizer for the REST API Gateway, that is configured to interact with the Cognito User Pool we just generated.

```
resource "aws_api_gateway_authorizer" "this" {
  name = var.domain_name
  rest_api_id = aws_api_gateway_rest_api.this.id
  type = "COGNITO_USER_POOLS"
  provider_arns = [var.cognito_arn]
}
```

#### modules/restApiGateway/outputs.tf

Once this API Gateway Authorizer is created, we then need to output some configuration variables so that it can be saved into an AWS Secret (next step) and retrieved by Serverless Framework for the Back End Auth.

```
output "root_resource_id" {
  value = aws_api_gateway_rest_api.this.root_resource_id
}

output "rest_api_id" {
  value = aws_api_gateway_rest_api.this.id
}

output "cognito_authorizer" {
  value = aws_api_gateway_authorizer.this.id
}
```

#### modules/secretsManager/main.tf

Lastly, we create an AWS Secret with all the exported outputs, so that the Front End and Back End have the configurations they need in order to interact with Amazon Cognito.

```
resource "aws_secretsmanager_secret" "this" {
  # Fill in the name you gave to your secret
  name = "${var.service_name}-tf"
  description = "Generated by Terraform for ${var.service_name}"

  recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "this" {
  secret_id = aws_secretsmanager_secret.this.id
  secret_string = jsonencode(local.secrets)
}

locals {
  secrets = merge(
    var.secrets,
    {
      next_cognito_identity_pool_id = "${var.cognito_identity_pool_id}"
      next_sls_cognito_user_pool_id = "${var.cognito_user_pool_id}"
      next_sls_cognito_user_pool_client_id = "${var.cognito_user_pool_client_id}"
      sls_cognito_authorizer = "${var.cognito_authorizer}"
      sls_cognito_user_pool_client_arn = "${var.cognito_user_pool_client_arn}"
      sls_api_gateway_root_resource_id = "${var.api_gateway_root_resource_id}"
      sls_api_gateway_rest_api_id = "${var.api_gateway_rest_api_id}"
    },
  )
}
```

### Conclusion

So this is how you use Terraform to create the Amazon Cognito resource and export its variables into a secret that can be used on the Front End and the Back End.

For the full codebase, check out [this link](https://github.com/JamesMillerBlog/wrapper.js/tree/main/templates/auth) and see [this documentation](https://jamesmiller.blog/wrapperjs-auth-template/) for further details on the [wrapper.js](https://jamesmiller.blog/wrapperjs/) template!

In the next post, I’ll talk through how to use these secrets on the Front End!

Until then, I hope this has been helpful and have fun :D

* * *