Set up AWS Cognito with Terraform and Go

Choosing AWS Cognito for your user authentication and authorization needs is an excellent option. Cognito provides a lot of capabilities, and with all the flexibility comes some complexity. It is hard to wrap your head around how to set it up, you probably have questions like:

The goal of this article is to shed some light on this topics and help you set Cognito for your project. We’ll use go for the examples, but should be able to understand the ideas behind the code an dapply them to a project using other languages.

Let’s start by discussing some basic topics, before we start creating terraform templates and writing go code.

AWS Cognito basic concepts

AWS Cognito provides a complete solution for authentication and authorization. Cognito can be used in different scenarios, for example:

User Pools

User pools are, as the name suggests, a repository of users you want to keep track off. The user pool can be an independent directory, that means that all the users live in the User Pool. The user pool can also source the users from third-party providers. That means that the User user pool will be an intermediate service provider. Cognito user pools can be used as an OIDC (OpenID Connect) Identity Provider.

The simplest form is to use the user pool as an independent directory. Everything is handled inside AWS Cognito, which makes things simpler. When using the user pools as an intermidate SP to third parties, help translating all the external tokens returned by the third-party IdPs to Cognito Tokens, so everything is standardised into one format and this could simplify your code.

App Client

In Cognito creating the User Pool is only half the batlte, you still need to interact with it. While you can in certain cases just interact with the User Pool programatically using the AWS SDK, it is far easier to interact with the User Pool via an app client. The app client provides features like the following:

In general you’ll need to set up an app client for your application to communicate with Cognito for user authentication, token retrieval and expirtation, etcetera.

Identity Pools

The identity pools are used in combination with authenticated users from User Pools, or it can use federated identities from external IdPs. When an entity has been authenticated, then we can provide AWS Credentials to access AWS resources. For example, we have an app that shows the content of a file in private S3 bucket. We can create an IAM role that grants access to the S3 bucket content. Instead of adding someone to your AWS organisation, you could use an Identity Pool to get temporary AWS credentials to assume that IAM role and be able to access the file form the private S3 bucket.

Identity Providers (IdPs)

A user pool can serve as an Identity Provider, meaning that it handles the verification of your users and can provide your app with information about them. When you use an external IdP, you can autenticate and get information about your users from a service that you don’t manage, it is also know as federated identities. You have probably seen sign in with Google, Facebook, Amazon, etcetera, they serve as Identity Providers. Their users are already registered in their service and their identity is being federated from them. You can also use an Active Directory as your identity provider, or any other service that can communicate using SAML, OAuth2.0 or OpenID Connect(OIDC).

Creating the user pool with terraform

First, we need to create our user pool. That is the easy part, the only required field is the name of the user pool:

1
2
3
4
5
resource "aws_cognito_user_pool" "user_pool" {
  name = var.user_pool_name
  
  alias_attributes = ["email"]
}

Adding the alias_attributes to include the email, means that the user would be able to login using its username or the email.

Now we need to set up our App client:

1
2
3
4
5
resource "aws_cognito_user_pool_client" "user_pool_client" {
  name            = var.user_pool_client_name
  user_pool_id    = aws_cognito_user_pool.user_pool.id
  generate_secret = var.generate_client_secret
}

We are not going to be using custom domains, so we just need a prefix for our domain:

1
2
3
4
5
6
7
resource "aws_cognito_user_pool_domain" "custom_domain" {
  domain       = var.custom_domain
  user_pool_id = aws_cognito_user_pool.user_pool.id
  # If we were to use a custom domain we would need the certificate to be managed
  # by AWS Certificate Manager, in us-east-1
  # certificate_arn = aws_acm_certificate.cert.arn
}

** If you want to check what else I'm currently doing, be sure to follow me on twitter @rderik or subscribe to the newsletter. If you want to send me a direct message, you can send it to derik@rderik.com.