Setting up AWS Cognito with Terraform
A comprehensive guide to configuring Amazon Cognito user authentication using Terraform Infrastructure as Code
Setting up AWS Cognito with Terraform
Amazon Cognito provides authentication, authorization, and user management for web and mobile apps. This guide shows how to set up Cognito using Terraform.
Prerequisites
- AWS CLI configured
- Terraform installed
- Basic understanding of authentication concepts
- Web or mobile application ready for integration
Project Structure
aws-cognito-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars
Basic Cognito Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# User Pool
resource "aws_cognito_user_pool" "main" {
name = "${var.project_name}-user-pool"
username_attributes = ["email"]
auto_verified_attributes = ["email"]
password_policy {
minimum_length = 8
require_lowercase = true
require_numbers = true
require_symbols = true
require_uppercase = true
}
verification_message_template {
default_email_option = "CONFIRM_WITH_CODE"
email_subject = "Account Verification Code"
email_message = "Your verification code is {####}"
}
schema {
attribute_data_type = "String"
name = "email"
required = true
mutable = true
string_attribute_constraints {
min_length = 7
max_length = 256
}
}
tags = {
Environment = var.environment
}
}
# App Client
resource "aws_cognito_user_pool_client" "main" {
name = "${var.project_name}-client"
user_pool_id = aws_cognito_user_pool.main.id
generate_secret = false
explicit_auth_flows = [
"ALLOW_USER_SRP_AUTH",
"ALLOW_REFRESH_TOKEN_AUTH"
]
}
# Identity Pool
resource "aws_cognito_identity_pool" "main" {
identity_pool_name = "${var.project_name}-identity-pool"
allow_unauthenticated_identities = false
cognito_identity_providers {
client_id = aws_cognito_user_pool_client.main.id
provider_name = aws_cognito_user_pool.main.endpoint
server_side_token_check = false
}
tags = {
Environment = var.environment
}
}
Advanced User Pool Configuration
# User Pool with Advanced Features
resource "aws_cognito_user_pool" "advanced" {
name = "${var.project_name}-advanced-pool"
# MFA Configuration
mfa_configuration = "ON"
software_token_mfa_configuration {
enabled = true
}
# Email Configuration
email_configuration {
email_sending_account = "DEVELOPER"
from_email_address = var.from_email
source_arn = var.ses_arn
}
# Lambda Triggers
lambda_config {
pre_sign_up = var.pre_signup_lambda_arn
post_confirmation = var.post_confirmation_lambda_arn
pre_authentication = var.pre_auth_lambda_arn
post_authentication = var.post_auth_lambda_arn
}
# Admin Create User Config
admin_create_user_config {
allow_admin_create_user_only = false
invite_message_template {
email_message = "Your username is {username} and temporary password is {####}."
email_subject = "Your temporary password"
sms_message = "Your username is {username} and temporary password is {####}."
}
}
# Device Configuration
device_configuration {
challenge_required_on_new_device = true
device_only_remembered_on_user_prompt = true
}
tags = {
Environment = var.environment
}
}
Variables Configuration
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "project_name" {
description = "Project name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
default = "dev"
}
variable "from_email" {
description = "From email address for Cognito emails"
type = string
}
variable "ses_arn" {
description = "SES ARN for email sending"
type = string
}
variable "pre_signup_lambda_arn" {
description = "Pre-signup Lambda function ARN"
type = string
}
Best Practices
-
User Pool Configuration
- Use strong password policies
- Enable MFA when possible
- Configure proper email verification
- Use custom attributes wisely
-
Security
- Implement proper OAuth flows
- Use secure authentication methods
- Monitor user activities
- Regular security reviews
-
User Experience
- Customize email templates
- Implement proper error handling
- Use friendly authentication flows
- Consider device tracking
-
Cost Optimization
- Monitor MAU usage
- Clean up unused pools
- Use appropriate features
- Consider pricing tiers
OAuth Configuration
# App Client with OAuth
resource "aws_cognito_user_pool_client" "oauth" {
name = "${var.project_name}-oauth-client"
user_pool_id = aws_cognito_user_pool.main.id
generate_secret = true
allowed_oauth_flows = [
"code",
"implicit"
]
allowed_oauth_flows_user_pool_client = true
allowed_oauth_scopes = [
"email",
"openid",
"profile"
]
callback_urls = [
"https://example.com/callback"
]
logout_urls = [
"https://example.com/logout"
]
supported_identity_providers = [
"COGNITO"
]
}
# Domain Configuration
resource "aws_cognito_user_pool_domain" "main" {
domain = var.domain_prefix
user_pool_id = aws_cognito_user_pool.main.id
}
Identity Pool Roles
# IAM Roles for Identity Pool
resource "aws_iam_role" "authenticated" {
name = "${var.project_name}-cognito-authenticated"
assume_role_policy = jsonencode({
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.main.id
}
"ForAnyValue:StringLike" = {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
})
}
resource "aws_iam_role" "unauthenticated" {
name = "${var.project_name}-cognito-unauthenticated"
assume_role_policy = jsonencode({
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.main.id
}
"ForAnyValue:StringLike" = {
"cognito-identity.amazonaws.com:amr": "unauthenticated"
}
}
}
]
})
}
# Identity Pool Role Attachment
resource "aws_cognito_identity_pool_roles_attachment" "main" {
identity_pool_id = aws_cognito_identity_pool.main.id
roles = {
authenticated = aws_iam_role.authenticated.arn
unauthenticated = aws_iam_role.unauthenticated.arn
}
}
Deployment Steps
- Initialize Terraform:
terraform init
- Plan the deployment:
terraform plan
- Apply the configuration:
terraform apply
Clean Up
Remove all resources when done:
terraform destroy
Common Use Cases
- Social Identity Providers
# Google Identity Provider
resource "aws_cognito_user_pool_identity_provider" "google" {
user_pool_id = aws_cognito_user_pool.main.id
provider_name = "Google"
provider_type = "Google"
provider_details = {
client_id = var.google_client_id
client_secret = var.google_client_secret
authorize_scopes = "email profile openid"
}
attribute_mapping = {
email = "email"
username = "sub"
}
}
# Facebook Identity Provider
resource "aws_cognito_user_pool_identity_provider" "facebook" {
user_pool_id = aws_cognito_user_pool.main.id
provider_name = "Facebook"
provider_type = "Facebook"
provider_details = {
client_id = var.facebook_client_id
client_secret = var.facebook_client_secret
authorize_scopes = "email public_profile"
}
attribute_mapping = {
email = "email"
username = "id"
}
}
- Custom Resource Server
# Resource Server
resource "aws_cognito_user_pool_resource_server" "main" {
identifier = "https://api.example.com"
name = "${var.project_name}-resource-server"
user_pool_id = aws_cognito_user_pool.main.id
scope {
scope_name = "read"
scope_description = "Read access"
}
scope {
scope_name = "write"
scope_description = "Write access"
}
}
User Groups
# User Group
resource "aws_cognito_user_group" "admin" {
name = "admin"
user_pool_id = aws_cognito_user_pool.main.id
description = "Managed by Terraform"
precedence = 1
role_arn = aws_iam_role.admin_group.arn
}
resource "aws_cognito_user_group" "users" {
name = "users"
user_pool_id = aws_cognito_user_pool.main.id
description = "Managed by Terraform"
precedence = 2
role_arn = aws_iam_role.user_group.arn
}
Conclusion
This setup provides a comprehensive foundation for deploying Cognito using Terraform. Remember to:
- Plan your authentication strategy carefully
- Implement proper security measures
- Configure user flows appropriately
- Keep your configurations versioned
- Test thoroughly before production deployment
The complete code can be customized based on your specific requirements and use cases.