Search code examples
swiftterraformamazon-cognitolocalstack

Cognito-signed-up users remain unverified despite auto_verified_attributes = ["email"]?


I have a localstack cognito user pool set up via the following .tf:

provider "aws" {
  region                  = "us-east-1"  # Change to your desired region
  access_key              = "test"       # Access key for LocalStack
  secret_key              = "test"       # Secret key for LocalStack
  skip_credentials_validation = true
  skip_requesting_account_id = true
  skip_metadata_api_check     = true

  endpoints {
    iam           = "http://localhost:4566"
    cognitoidp   = "http://localhost:4566"
  }
}
resource "aws_cognito_user_pool" "main_user_pool" {
  name = "main_user_pool"

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

    recovery_mechanism {
      name     = "verified_phone_number"
      priority = 2
    }
  }

  # Define the attributes for the user pool
  # Ensure that only standard attributes are marked as required
  schema {
    name                = "email"
    attribute_data_type = "String"
    mutable             = true
    required            = true
  }

  


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

  email_configuration {
    email_sending_account = "COGNITO_DEFAULT"
  }

  username_attributes = ["email"]
  auto_verified_attributes = ["email"]
# allow users to sign in with their email as well as their username

  username_configuration {
    case_sensitive = true
  }
}

resource "aws_cognito_user_pool_client" "userpool_client" {
  name                          = "my-client"
  user_pool_id                  = aws_cognito_user_pool.main_user_pool.id
  generate_secret               = false  # No client secret for a public client like a mobile app
}


output "user_pool_client_id" {
  value = aws_cognito_user_pool_client.userpool_client.id
}

I have the line auto_verified_attributes = ["email"] and so I expected any signed-up user to become immediately verified by default.

I have my swift app sign up users with the following code:

func registerUser(email: String, password: String) {
        let serviceConfiguration = AWSServiceConfiguration(region: .USEast1,
                                                           credentialsProvider: nil)
        AWSServiceManager.default().defaultServiceConfiguration = serviceConfiguration

        let signUpRequest = AWSCognitoIdentityProviderSignUpRequest()!
        signUpRequest.clientId = CognitoConfig.clientId
        signUpRequest.username = email
        signUpRequest.password = password
        
        // Define user attributes here - e.g., email
        let emailAttribute = AWSCognitoIdentityUserAttributeType()
        emailAttribute?.name = "email"
        emailAttribute?.value = email
        
        signUpRequest.userAttributes = [emailAttribute!]
        
        // Get the service provider instance
        let cognitoProvider = AWSCognitoIdentityProvider(forKey: "LocalStackCognito")
        
        // Perform the sign-up
        cognitoProvider.signUp(signUpRequest).continueWith { task -> AnyObject? in
            DispatchQueue.main.async {
                if let error = task.error {
                    print("Registration Error: \(error)")
                    //passwrod too short pass doesn't seem human readable
                    errorMessage = error.localizedDescription
                } else {
                    print("Registration Success")
                    
                    loginUser(email: email, password: password)
                    // Handle successful registration, e.g., show confirmation code UI.
                }
            }
            return nil
        }
    }

If I describe the pool via awslocal cognito-idp describe-user-pool --user-pool-id "<userpoolid>" I can see in the response that the email is an autoverified attribute:

...
,
        "AutoVerifiedAttributes": [
            "email"
        ],
        "UsernameAttributes": [
            "email"
        ],
        "VerificationMessageTemplate": {
            "DefaultEmailOption": "CONFIRM_WITH_CODE"
        },
        "UserAttributeUpdateSettings": {
            "AttributesRequireVerificationBeforeUpdate": []
        },
...

However when I sign up and then list the users via awslocal cognito-idp list-users --user-pool-id "<userpoolid>" I get:

{
    "Users": [
        {
            "Username": "141bd541-2d8a-4c55-8ebd-83296e1ff99b",
            "Attributes": [
                {
                    "Name": "cognito:username",
                    "Value": "[email protected]"
                },
                {
                    "Name": "email",
                    "Value": "[email protected]"
                },
                {
                    "Name": "sub",
                    "Value": "141bd541-2d8a-4c55-8ebd-83296e1ff99b"
                },
                {
                    "Name": "email_verified",
                    "Value": "false"
                }
            ],
            "UserCreateDate": 1710631590.379655,
            "UserLastModifiedDate": 1710631590.379655,
            "Enabled": true,
            "UserStatus": "UNCONFIRMED"
        }
    ]
}

The email_verified is false and the UserStatus is UNCONFIRMED. I don't understand what I am doing wrong. The terraform appears set up correctly and I can verify it via the pool description. What am I missing?


Solution

  • Sigh. Turns out the entire premise of my question is wrong. There is (as far as I can tell reading the docs more thoroughly) no way to IaC define the user pool to automatically verify users. Hence, the solution exists not in the terraform, but in manually confirming the user after registration via adminConfirmSignUp.

    func confirmUser(email: String) {
            let confirmSignUpRequest = AWSCognitoIdentityProviderAdminConfirmSignUpRequest()!
            confirmSignUpRequest.userPoolId = "<user_pool_id>"
            confirmSignUpRequest.username = email
    
            let cognitoProvider = AWSCognitoIdentityProvider(forKey: "LocalStackCognito")
            cognitoProvider.adminConfirmSignUp(confirmSignUpRequest).continueWith { task -> AnyObject? in
                DispatchQueue.main.async {
                    if let error = task.error {
                        print("Confirmation Error: \(error)")
                        // Handle confirmation error
                    } else {
                        print("User Confirmed")
                        // User is confirmed, proceed with login or next steps
                    }
                }
                return nil
            }
        }
    

    This of course wouldn't be implemented in production code. For dev only.