Auth0 custom domain with Cloudflare and Terraform
When you use Auth0 for authentication, you can use a custom domain to make your login page respect your branding and look more professional. This is usually accomplished with a simple CNAME record pointing to the Auth0 domain. It's a straightforward process if you use Auth0's managed certificates. However, depending on the popularity of your service you might find yourself under auth attacks that might cause your auth to be down. The way to fix this is by proxying your auth through Cloudflare.
This is a simple process, but the docs have a number of errors that do get on the way. Here's how to do it with terraform:
variable "custom_domain" {
type = string
default = "auth.myservice.com"
}
variable "cloudflare_account_id" {
type = string
default = "1234567890"
}
variable "cloudflare_zone_id" {
type = string
default = "1234567890"
}
variable "tenant" {
type = string
default = "myservice-prod"
}
# first create the custom domain
resource "auth0_custom_domain" "custom_domain" {
domain = var.custom_domain
custom_client_ip_header = "true-client-ip"
type = "self_managed_certs"
}
# then create the verification record
resource "cloudflare_record" "custom_domain_verification_record" {
zone_id = var.cloudflare_zone_id
name = "_cf-custom-hostname.${var.tenant}"
value = auth0_custom_domain.custom_domain.verification[0].methods[0].record
type = "TXT"
depends_on = [auth0_custom_domain.custom_domain]
}
# verify the custom domain
resource "auth0_custom_domain_verification" "custom_domain_verification" {
custom_domain_id = auth0_custom_domain.custom_domain.id
timeouts { create = "15m" }
depends_on = [cloudflare_record.custom_domain_verification_record]
}
# create a CNAME record for the custom domain pointing to the Auth0 origin returned by custom domain verification
resource "cloudflare_record" "custom_domain_record" {
zone_id = var.cloudflare_zone_id
name = auth0_custom_domain.custom_domain.domain
value = auth0_custom_domain_verification.custom_domain_verification.origin_domain_name
type = "CNAME"
proxied = true
depends_on = [auth0_custom_domain_verification.custom_domain_verification]
}
# create a page rule that overrides the host header, passing the True-Client-IP header
resource "cloudflare_page_rule" "reverse_proxy" {
zone_id = var.cloudflare_zone_id
target = "${var.custom_domain}/*"
actions {
host_header_override = auth0_custom_domain_verification.custom_domain_verification.origin_domain_name
true_client_ip_header = "on"
}
depends_on = [auth0_custom_domain_verification.custom_domain_verification]
}
# create a worker that will reverse proxy the request to the Auth0 origin
resource "cloudflare_worker_script" "reverse_proxy" {
account_id = var.cloudflare_account_id
name = "reverse-proxy-worker-${var.tenant}"
content = templatefile("${path.module}/files/workers/reverse_proxy.js", {
cname_api_key = auth0_custom_domain_verification.custom_domain_verification.cname_api_key
})
depends_on = [auth0_custom_domain_verification.custom_domain_verification]
}
# create a worker route that will route all requests to the worker
resource "cloudflare_worker_route" "reverse_proxy" {
zone_id = var.cloudflare_zone_id
pattern = "${var.custom_domain}/*"
script_name = cloudflare_worker_script.reverse_proxy.name
depends_on = [cloudflare_worker_script.reverse_proxy]
}
For the worker script, you can use the following:
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
request = new Request(request);
request.headers.set('cname-api-key', '${cname_api_key}');
return await fetch(request);
}
With this setup, you can now use your custom domain proxied through Cloudflare for Auth0 authentication.