Customising AWS Cognito Hosted UI with Terraform

Published on 5 months agoCustomising AWS Cognito Hosted UI with Terraform

What is AWS Cognito?

AWS Cognito is an Amazon service that authenticates your web and mobile applications, devices, and services. It also allows sign-in through third-party providers such as Facebook, Google, Apple, etc. In addition, AWS Cognito also provides 50K free monthly active users, which is competitive pricing compared to other authentication providers like Auth0, OKTA, etc.

Why use AWS Cognito Hosted UI?

We can also add a custom domain to AWS Cognito to use the OAuth 2.0-compliant authorization server and ready-to-use UI for authentication. The hosted UI included sign-in, sign-up, forgot password, account verification, third-party authentication, and multifactor authentication. Building our OAuth 2.0 authorization is always complex and expensive, but we can use the AWS Cognito-hosted UI for free.

Why not use AWS Cognito Hosted UI?

Although AWS Cognito is a secure and production-ready authentication and authorization solution, the hosted UI still has many limitations when customising it to suit our brand. Here are a few of these limitations.

  1. It supports only English and not other languages.
  2. The style customisations are limited to free classes. See the documentation about customising the AWS Cognito.
  3. Only required standard user attributes can be shown in the sign-up form. See the documentation about the standard user attributes.
  4. The account verification flow is broken if the user closes the page without verifying their account using a verification code. We can opt-in to use link verification to allow users to click the link from their email or SMS to verify their account, but the link will expire after 24 hours.
  5. We cannot change any text messages or labels in the hosted UI.

You can try it if you would like to trade off the user experience during sign-up. Otherwise, an AWS Cognito-hosted UI may not be suitable for you.

Creating AWS Cognito Hosted UI with Terraform

If you decide to proceed with AWS Cognito, we can create the AWS Cognito with a hosted UI. We will use Terraform to automate the creation of the service. In summary, we need to create a few AWS services using Terraform.

  1. AWS Cognito Pool
  2. AWS Cognito Pool Client
  3. Custom domain for AWS Cognito Pool Client, including domain verification
  4. AWS Certificate Manager's certificate for the AWS Cognito Pool Client's custom domain

Create a cognito.tf and paste the following codes.

# Create AWS Cognito User Pool
resource "aws_cognito_user_pool" "main" {
  name = "main"

  username_attributes = ["email"]

  # Use AWS SES to send emails
  email_configuration {
    email_sending_account = "DEVELOPER"
    # TODO: Change this to your email address
    from_email_address = "My Website "
    # TODO: Change this to your AWS SES domain identity ARN
    source_arn = aws_ses_domain_identity.main.arn
  }
  auto_verified_attributes = ["email"]
  user_attribute_update_settings {
    attributes_require_verification_before_update = ["email"]
  }

  # Verify email by sending a link to avoid broken flows of the hosted UI
  verification_message_template {
    default_email_option = "CONFIRM_WITH_LINK"
  }
  deletion_protection = "ACTIVE"
}

# Create AWS Cognito User Pool Client
resource "aws_cognito_user_pool_client" "main" {
  name = "main"

  generate_secret     = true
  user_pool_id = aws_cognito_user_pool.main.id
  allowed_oauth_flows_user_pool_client = true
  allowed_oauth_flows                  = ["code", "implicit"]
  allowed_oauth_scopes                 = ["email", "openid", "phone", "profile"]
  supported_identity_providers = ["COGNITO"]
  # TODO: Change this to your login callback URL
  callback_urls = ["https://mywebsite.com/api/auth/callback/cognito"]
  # TODO: Change this to your logout URL
  logout_urls = ["https://mywebsite.com"]
}

# Create AWS Cognito User Pool Domain
resource "aws_cognito_user_pool_domain" "main" {
  # TODO: Change this to your own domain to host the Cognito UI
  domain       = "auth.mywebsite.com"
  certificate_arn = aws_acm_certificate.main.arn
  user_pool_id = aws_cognito_user_pool.main.id

  depends_on = [aws_acm_certificate_validation.main]
}

# Create AWS ACM Certificate for the custom domain
resource "aws_acm_certificate" "main" {
  # TODO: Change this to your own domain to host the Cognito UI, similar to the domain above
  domain_name       = "auth.mywebsite.com"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

# Create Route53 Record for the ACM Certificate verfication
resource "aws_route53_record" "main" {
  for_each = {
    for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = data.aws_route53_zone.main.zone_id
}

# Validate the ACM Certificate
resource "aws_acm_certificate_validation" "main" {
  certificate_arn         = aws_acm_certificate.main.arn
  validation_record_fqdns = [for record in aws_route53_record.main : record.fqdn]
}

# Create Route53 Record for the Cognito User Pool Domain
resource "aws_route53_record" "main_a" {
  name    = aws_cognito_user_pool_domain.main.domain
  type    = "A"
  zone_id = data.aws_route53_zone.main.zone_id
  alias {
    evaluate_target_health = false

    name    = aws_cognito_user_pool_domain.main.cloudfront_distribution
    zone_id = aws_cognito_user_pool_domain.main.cloudfront_distribution_zone_id
  }
}

You should change all the values that are marked as // TODO in the code above.

Once you apply all the changes, you should able to access the hosted UI in this URL, and it should be the image below:

https:///oauth2/authorize?response_type=code&client_id=&redirect_uri=
AWS Cognito Hosted UI without customisation

Customising the AWS Cognito Hosted UI

We have successfully set up the AWS Cognito-hosted UI, and we should be able to use it to sign up, verify an account, and sign in. However, the UI does not look good, so we need to customise the style to suit our brand. AWS only allows us to use specific CSS classes to customise the UI. We can also sign in to the AWS console to download the default CSS template file for customisation. Go to Cognito -> YOUR COGNITO USER POOL -> Click on the App Integration tab -> Click on the Edit button in "Hosted UI customization".

You will see a CSS file link that allows you to download the default CSS template file to ease customisation. On the same page, we can upload our website logo, which will be displayed in the hosted UI.

Let's update the CSS file to look like the style below.

Update the CSS file to the following.

.logo-customizable {
  max-width: 60%;
  max-height: 30%;
}

.banner-customizable {
  padding: 50px 0px 16px 0px;
  background-color: transparent;
  box-shadow: none;
  border-radius: 8px;
}

.label-customizable {
  font-weight: 500;
  font-size: 16px;
  margin-top: 16px;
}

.textDescription-customizable {
  padding-top: 0px;
  padding-bottom: 0px;
  display: block;
  font-size: 16px;
}

.idpDescription-customizable {
  padding-top: 10px;
  padding-bottom: 10px;
  display: block;
  font-size: 16px;
}

.legalText-customizable {
  color: #747474;
  font-size: 11px;
}

.submitButton-customizable {
  font-size: 18px;
  font-weight: 500;
  margin: 20px 0px 10px 0px;
  height: 48px;
  width: 100%;
  color: #fff;
  background-color: #3e63dd;
}

.submitButton-customizable:hover {
  color: #fff;
  background-color: #3358d4;
}

.errorMessage-customizable {
  padding: 16px;
  font-size: 14px;
  width: 100%;
  background: #ffebeb;
  color: #ce2c31;
  border: none;
  border-radius: 8px;
  margin-bottom: 0px;
  margin-top: 12px;
}

.inputField-customizable {
  width: 100%;
  height: 38px;
  color: #000;
  background-color: #fff;
  border: 1px solid #e5e7eb;
  font-size: 16px;
  box-shadow: none;
}

.inputField-customizable:focus {
  border-color: #3e63dd;
  outline: 0;
}

.idpButton-customizable {
  height: 40px;
  width: 100%;
  width: 100%;
  text-align: center;
  margin-bottom: 15px;
  color: #fff;
  background-color: #3e63dd;
  border-color: #3e63dd;
}

.idpButton-customizable:hover {
  color: #fff;
  background-color: #3358d4;
}

.socialButton-customizable {
  border-radius: 2px;
  height: 40px;
  margin-bottom: 15px;
  padding: 1px;
  text-align: left;
  width: 100%;
}

.redirect-customizable {
  font-size: 16px;
  text-align: center;
}

.passwordCheck-notValid-customizable {
  color: #ce2c31;
}

.passwordCheck-valid-customizable {
  color: #19bf00;
}

.background-customizable {
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0px 2px 20px rgba(0, 0, 0, 0%), 0 0 0 1000000px #fff;
  border: 1px solid #e5e7eb;
}

Next, we need to update the cognito.tf to include the customisation:

// Other codes

# Add the customisation
resource "aws_cognito_user_pool_ui_customization" "main" {
  # TODO: The path to your custom CSS file
  css = file("cognito-ui.css")
  # TODO: The path to your custom logo
  image_file = filebase64("cognito-logo.jpg")

  user_pool_id = aws_cognito_user_pool_domain.main.user_pool_id
}

Last but not least, commit your terraform codes and apply them. Refresh your AWS Cognito-hosted UI, and you should see something like this, apart from the logo that should be your own uploaded logo.

Conclusion

In my opinion, AWS Cognito-hosted UI could be a good starter for small and medium businesses that would like to adopt OAuth 2.0 authorization. However, we need to work around the limitations of the hosted UI. AWS Cognito is an excellent service provided by AWS, but unfortunately, they are not prioritising the improvement of the hosted UI, which would make everything perfect if they did.

Copyright © 2024 Tek Min Ewe