かなり沼にハマりましたが、TerraformでAmazon CognitoのHosted UIを独自ドメインでサーブするところまでを構築できました。
実装はこちら。
構築のハマりどころ
構築自体は、aws_cognito_user_pool
、aws_cognito_user_pool_client
の双方を設定すればそれほど時間がかかるわけではありません。
かなりハマったのは、独自ドメインの設定でした。
Hosted UIに割り当てる証明書はバージニアで作成する
CognitoのHosted UIはCloudFront経由でサーブされます。
CloudFrontにACMで発行された証明書を割り当てる場合、当該の証明書はバージニア(us-east-1
)で構成しなければなりません。
以下はCloudFrontのマニュアルから。
To use an ACM certificate with CloudFront, make sure you request (or import) the certificate in the US East (N. Virginia) Region (
us-east-1
).
同様のことが、Cognitoのマニュアルにも記載されています。
You must change the AWS region to US East (N. Virginia) in the ACM console before you request or import a certificate.
これをTerraformで実現するためには、まずus-east-1
用のProviderを作成しなければなりません。
provider "aws" { alias = "acm_provider" region = "us-east-1" }
その上で、当該リージョンにおいて、証明書やその検証用のRoute 53レコードを作成していく必要があります。
provider = aws.acm_provider
あたりがその記述ですね。
resource "aws_acm_certificate" "this" { provider = aws.acm_provider # 最後にピリオドをつけてはいけない domain_name = local.hosted_ui_fqdn validation_method = "DNS" options { certificate_transparency_logging_preference = "ENABLED" } lifecycle { create_before_destroy = true } } # 証明書発行時の検証用に利用する DNS レコード resource "aws_route53_record" "validation" { provider = aws.acm_provider for_each = { for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } zone_id = data.aws_route53_zone.this.id name = each.value.name type = each.value.type ttl = 60 records = [each.value.record] allow_overwrite = true } resource "aws_acm_certificate_validation" "this" { provider = aws.acm_provider certificate_arn = aws_acm_certificate.this.arn validation_record_fqdns = [for record in aws_route53_record.validation : record.fqdn] }
Hosted UIに割り当てる独自ドメインの1つ上の階層にAレコードが必要
これは本当に意味がわかっていないのですが、Hosted UIに割り当てるドメインの1つ上のドメイン階層に、Aレコードが存在していることが必要です。
If you enter
auth.example.com
, you need an A record forexample.com
If you enter
auth.qa.example.com
, you need an A record forqa.example.com
If you enter
foo.bar.qa.example.com
you need an A record forbar.qa.example.com
AWSのマニュアルでは以下のように記述されています。
A web domain that you own. Its root must have a valid A record in DNS. For example, if your custom domain is
auth.example.com
, you must be able to resolveexample.com
to an IP address.
なぜこのAレコードが前提になっているのが本当に意味がわからないのですが、とにかくどんな値であってもAレコードがあればOKです。
ぼくはこんな形で、127.0.0.1
へ向けたダミーのAレコードを追加して回避しました。
# `custom_domain` に紐づく DNS ゾーンに作成するダミーの A レコード # 独自ドメインを設定する前提条件として、この A レコードが必要。 # # see: https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html#cognito-user-pools-add-custom-domain-adding resource "aws_route53_record" "dummy" { count = var.create_dummy_record ? 1 : 0 zone_id = data.aws_route53_zone.this.id name = var.custom_domain type = "A" ttl = 60 records = ["127.0.0.1"] }
ログインしてみる
今回はクライアントがSPAであることを想定し、Authorization Code Grantを利用する形にしました。
resource "aws_cognito_user_pool_client" "name" { name = "client" user_pool_id = aws_cognito_user_pool.this.id # SPA なので、クライアントシークレットを発行したとしてもセキュアに守れない generate_secret = false prevent_user_existence_errors = "ENABLED" token_validity_units { access_token = "minutes" id_token = "minutes" refresh_token = "days" } access_token_validity = 60 # 分 id_token_validity = 60 # 分 callback_urls = [ "http://localhost:8080/" ] allowed_oauth_flows = ["code"] explicit_auth_flows = [ "ALLOW_USER_SRP_AUTH", "ALLOW_REFRESH_TOKEN_AUTH" ] supported_identity_providers = [ "COGNITO" ] allowed_oauth_scopes = ["openid"] allowed_oauth_flows_user_pool_client = true }
このクライアントからHosted UIに接続する形で実際にログインをしてみます。
Hosted UI経由でログインしてみると、ログイン後たしかに認可コードが発行されていることが確認できました。