hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.85k stars 9.19k forks source link

[Bug]: Incompatible client_id for Google OAuth in AWS Cognito Identity Pool #39560

Open KenkoGeek opened 1 month ago

KenkoGeek commented 1 month ago

Terraform Core Version

1.5.5

AWS Provider Version

5.69.0

Affected Resource(s)

There is an error when trying to integrate Google OAuth with aws_cognito_identity_pool using the client_id from Google’s OAuth credentials.

resource "aws_cognito_identity_pool" "this" {
  identity_pool_name               = "${var.project_name}-identity-pool"
  allow_unauthenticated_identities = false

  # Cognito as identity provider
  cognito_identity_providers {
    client_id     = aws_cognito_user_pool_client.this.id
    provider_name = aws_cognito_user_pool.this.endpoint
  }

  External identity providers
   dynamic "cognito_identity_providers" {
     for_each = var.identity_providers
     content {
       client_id     = cognito_identity_providers.value.client_id
       provider_name = cognito_identity_providers.value.provider_name
     }
  }

  tags = {
    Name = "${var.project_name}-identity-pool"
  }
}
variable "identity_providers" {
  description = "Map of configuration for Cognito Identity Providers"
  type = map(object({
    client_id         = string
    client_secret     = string
    provider_name     = string
    provider_type     = string
    authorize_scopes  = string
    additional_details = map(string)
  }))
  default = {}
}
identity_providers = {
  google = {
    client_id        = "xx33eeexxxaaa.apps.googleusercontent.com "
    client_secret    = "GOaassxxddffccvvkkkk"
    provider_name    = "accounts.google.com"
    provider_type    = "Google"
    authorize_scopes = "openid,email,profile"
    additional_details = {
      project_id = "my_project_sknndhnhj123"
    }
  }
}

Problem Overview:

When setting up Google OAuth as a login provider for an aws_cognito_identity_pool, the client_id obtained from Google Cloud Console (for example, 123456789012.apps.googleusercontent.com) causes the following error:

"client_id must contain only alphanumeric characters and underscores"

However, this is the official OAuth Client ID format from Google, and Cognito expects this value when integrating Google OAuth. This issue arises due to the restriction that aws_cognito_identity_pool imposes on the client_id, where only alphanumeric characters and underscores are allowed. This limitation conflicts with the structure of Google’s OAuth Client IDs, which include periods and hyphens.

Error Output:

Error: "cognito_identity_providers.1.client_id" must contain only alphanumeric characters and underscores

  with aws_cognito_identity_pool.main,
  on main.tf line 88, in resource "aws_cognito_identity_pool" "main":
  88: resource "aws_cognito_identity_pool" "this" {

Expected Behavior

The aws_cognito_identity_pool should accept Google’s OAuth Client ID format without throwing an error, allowing integration of Google OAuth for user login.

In AWS Console and CDK is supported.

Actual Behavior

The Identity Pool rejects the Google client_id due to non-alphanumeric characters such as periods (.) and hyphens (-), even though this is the official format used by Google for OAuth Client IDs.

OAuth Client ID Format: Google uses xxx.apps.googleusercontent.com, which is not compliant with the restrictions currently imposed by AWS Cognito Identity Pool.

Relevant Error/Panic Output Snippet

Terraform planned the following actions, but then encountered a problem:

  # module.wp_butler_cognito.aws_cognito_identity_provider.these["google"] must be replaced
-/+ resource "aws_cognito_identity_provider" "these" {
      ~ attribute_mapping = {
          - "username" = "sub"
        } -> (known after apply)
      ~ id                = "us-east-2_LXXRRTT:Google" -> (known after apply)
      - idp_identifiers   = [] -> null
      ~ provider_details  = {
          - "attributes_url"                = "https://people.googleapis.com/v1/people/me?personFields=" -> null
          - "attributes_url_add_attributes" = "true" -> null
          - "authorize_url"                 = "https://accounts.google.com/o/oauth2/v2/auth" -> null
          - "oidc_issuer"                   = "https://accounts.google.com" -> null
          - "token_request_method"          = "POST" -> null
          - "token_url"                     = "https://www.googleapis.com/oauth2/v4/token" -> null
            # (3 unchanged elements hidden)
        }
      ~ provider_name     = "Google" -> "accounts.google.com" # forces replacement
        # (2 unchanged attributes hidden)
    }

Plan: 1 to add, 0 to change, 1 to destroy.
╷
│ Error: "cognito_identity_providers.1.client_id" must contain only alphanumeric characters and underscores
│ 
│   with module.wp_butler_cognito.aws_cognito_identity_pool.this,
│   on .terraform/modules/wp_butler_cognito/main.tf line 88, in resource "aws_cognito_identity_pool" "this":
│   88: resource "aws_cognito_identity_pool" "this" {
│ 
╵
::error::Terraform exited with code 1.
Error: Process completed with exit code 1.

Terraform Configuration Files

resource "aws_cognito_user_pool" "this" {
  name = "${var.project_name}-user-pool"

  auto_verified_attributes = ["email"]
  username_attributes      = ["email"]
  mfa_configuration        = "ON"

  password_policy {
    minimum_length    = 12
    require_lowercase = true
    require_numbers   = true
    require_symbols   = true
    require_uppercase = true
  }

  software_token_mfa_configuration {
    enabled = true
  }

  account_recovery_setting {
    recovery_mechanism {
      name     = "verified_email"
      priority = 1
    }
  }

  device_configuration {
    challenge_required_on_new_device = true
  }

  tags = {
    Name = "${var.project_name}-user-pool"
  }
}

# Cognito Identity Providers configuration
resource "aws_cognito_identity_provider" "these" {
  for_each = var.identity_providers

  user_pool_id  = aws_cognito_user_pool.this.id
  provider_name = each.value.provider_type
  provider_type = each.value.provider_type

  provider_details = {
    client_id        = each.value.client_id
    client_secret    = each.value.client_secret
    authorize_scopes = each.value.authorize_scopes
    team_id          = lookup(each.value.additional_details, "team_id", null)
    key_id           = lookup(each.value.additional_details, "key_id", null)
    private_key      = lookup(each.value.additional_details, "private_key", null)
  }
}

# Introduce a wait time before creating the User Pool Client
resource "null_resource" "wait_before_client_creation" {
  provisioner "local-exec" {
    command = "sleep 30"
  }
}

# Cognito User Pool Client configuration
resource "aws_cognito_user_pool_client" "this" {
  depends_on = [null_resource.wait_before_client_creation]

  user_pool_id                         = aws_cognito_user_pool.this.id
  name                                 = "${var.project_name}-app-client"
  allowed_oauth_flows                  = var.allowed_oauth_flows
  allowed_oauth_flows_user_pool_client = var.allowed_oauth_flows_user_pool_client
  allowed_oauth_scopes                 = var.allowed_oauth_scopes
  callback_urls                        = var.callback_urls
  logout_urls                          = var.logout_urls
  generate_secret                      = false

  supported_identity_providers = flatten([
    "COGNITO",
    [
      for p in keys(var.identity_providers) :
      p == "facebook" ? "Facebook" : (
        p == "google" ? "Google" : (
      p == "apple" ? "SignInWithApple" : null))
      if p != null
    ]
  ])
}

# Cognito Identity Pool configuration
resource "aws_cognito_identity_pool" "this" {
  identity_pool_name               = "${var.project_name}-identity-pool"
  allow_unauthenticated_identities = false

  # Cognito as identity provider
  cognito_identity_providers {
    client_id     = aws_cognito_user_pool_client.this.id
    provider_name = aws_cognito_user_pool.this.endpoint
  }

  # External identity providers
  dynamic "cognito_identity_providers" {
    for_each = var.identity_providers
    content {
      client_id     = cognito_identity_providers.value.client_id
      provider_name = cognito_identity_providers.value.provider_name
    }
  }

  tags = {
    Name = "${var.project_name}-identity-pool"
  }
}

resource "aws_iam_role" "authenticated_role" {
  name = "${var.project_name}-authenticated-role"

  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.this.id
          },
          "ForAnyValue:StringLike" : {
            "cognito-identity.amazonaws.com:amr" : "authenticated"
          }
        }
      }
    ]
  })
}

resource "aws_cognito_identity_pool_roles_attachment" "this" {
  identity_pool_id = aws_cognito_identity_pool.this.id

  roles = {
    authenticated = aws_iam_role.authenticated_role.arn
  }
}

Steps to Reproduce

1.  Register an application in Google Cloud Console and obtain an OAuth Client ID.
•   Format of the OAuth Client ID: 123456789012.apps.googleusercontent.com.
2.  Set up an `aws_cognito_identity_pool` and configure Google OAuth as a Login Provider using the OAuth Client ID obtained from Google Cloud.
3.  Run the Terraform configuration
4.  Observe the error due to the restriction on the format of client_id.

Debug Output

No response

Panic Output

No response

Important Factoids

No response

References

No response

Would you like to implement a fix?

No

github-actions[bot] commented 1 month ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue