Skip to content

AWS Cognito returns AliasExistsException incorrectly.

0

Hi, we are using the latest version of the .net AWS SDK. This question relates to AWS Cognto so the nuget package being used are these ones -

We have a Cognito user pool set up in this way:

  • Cognito user pool sign-in options: Email (so email address is an alias for username)
  • Required attributes - email, given_name, family_name
  • Keep original attribute value active when an update is pending: Disabled
  • Attributes to verify: Send email message, verify email address
  • Password sign-in only
  • We have also set up Custom Email Sender Lambda trigger and Custom SMS Sender Lambda trigger

Users can be created through self sign-up on our web app (which uses AWS Amplify) or users can also be created through our back-end which is .net 8.0 and uses the above mentioned AWS SDK Nuget packages.

Through the back-end it is also possible to update a user's email address. This uses the following SDK methods -

  • IAmazonCognitoIdentityProvider.AdminGetUserAsync
  • IAmazonCognitoIdentityProvider.AdminUpdateUserAttributesAsync

When we call AdminUpdateUserAttributesAsync to update the email address for a user (which is an alias for username), we always get an AliasExistsException with a message of An account with the given email already exists. - even if there is no user with the email address we are changing to. Is there any reason for this to happen?

We have tried it with multiple user pools, multiple users, users that are in FORCE_CHANGE_PASSWORD as well as users in CONFIRMED - always the same result.

When we update email address via the AWS Console or via the CLI it seems to work fine. It's only via the .net SDK that it fails with the exception.

The code looks a bit like this (there is more going on in our actual code but the AWS calls are effectively the same) -

    var adminGetUserResponse = await _amazonCognitoIdentityProvider.AdminGetUserAsync(
        new AdminGetUserRequest
        {
            UserPoolId = userPoolId,
            Username = currentEmailAddress,
        });

    List<AttributeType> userAttributeTypes =
    [
        new AttributeType { Name = "family_name", Value = updatedFamilyName },
        new AttributeType { Name = "given_name", Value = updatedGivenName },
        new AttributeType { Name = "email_verified", Value = true }
    ];

    Dictionary<string, string> clientMetadata = [];
    if (!string.IsNullOrWhiteSpace(updatedEmailAddress) &&
        !string.Equals(currentEmailAddress, updatedEmailAddress, StringComparison.OrdinalIgnoreCase))
    {
        userAttributeTypes.Add(new AttributeType { Name = Constants.EmailAttribute, Value = updatedUsername });
        clientMetadata.Add("PreviousEmailAddress", currentEmailAddress);
    }
    if (!string.IsNullOrWhiteSpace(updatedPhoneNumber))
    {
        userAttributeTypes.Add(
            new AttributeType { Name = Constants.PhoneNumberAttribute, Value = updatedPhoneNumber });
    }

    await _amazonCognitoIdentityProvider.AdminUpdateUserAttributesAsync(
        new AdminUpdateUserAttributesRequest
        {
            UserPoolId = userPoolId,
            Username = adminGetUserResponse.UserAttributes.First(at => at.Name == "sub").Value,
            UserAttributes = userAttributeTypes,
            ClientMetadata = clientMetadata,
        });

The exception trace looks like this -

Amazon.CognitoIdentityProvider.Model.AliasExistsException: An account with the given email already exists.
 ---> Amazon.Runtime.Internal.HttpErrorResponseException: Exception of type 'Amazon.Runtime.Internal.HttpErrorResponseException' was thrown.
   at Amazon.Runtime.HttpWebRequestMessage.ProcessHttpResponseMessage(HttpResponseMessage responseMessage)
   at Amazon.Runtime.HttpWebRequestMessage.GetResponseAsync(CancellationToken cancellationToken)
   at Amazon.Runtime.Internal.HttpHandler`1.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.Unmarshaller.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ErrorHandler.InvokeAsync[T](IExecutionContext executionContext)
   --- End of inner exception stack trace ---
   at Amazon.Runtime.Internal.HttpErrorResponseExceptionHandler.HandleExceptionStream(IRequestContext requestContext, IWebResponseData httpErrorResponse, HttpErrorResponseException exception, Stream responseStream)
   at Amazon.Runtime.Internal.HttpErrorResponseExceptionHandler.HandleExceptionAsync(IExecutionContext executionContext, HttpErrorResponseException exception)
   at Amazon.Runtime.Internal.ExceptionHandler`1.HandleAsync(IExecutionContext executionContext, Exception exception)
   at Amazon.Runtime.Internal.ErrorHandler.ProcessExceptionAsync(IExecutionContext executionContext, Exception exception)
   at Amazon.Runtime.Internal.ErrorHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.Signer.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.EndpointDiscoveryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.EndpointDiscoveryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CredentialsRetriever.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ErrorCallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.MetricsHandler.InvokeAsync[T](IExecutionContext executionContext)
... <goes into our code here>

For reference, here is the AWS CLI call on the same user pool which succeeds. This looks to be equivalent to what our code is doing.

aws --profile <awsProfile> cognito-idp admin-update-user-attributes \
  --user-pool-id <userPoolIId> \
  --username <subAttribute> \
  --user-attributes Name=email,Value=<updatedEmailAddress> Name=email_verified,Value=true Name=phone_number,Value=<updatedPhoneNumber> Name=family_name,Value=<familyName> Name=given_name,Value=<givenName>

Are we missing something obvious? Or not so obvious? Or is there a bug with the AWS SDK?

2 Answers
0
Accepted Answer

This turned out to be an internal issue to our codebase. When we ran the above code the problem did indeed manifest but it was not because of this code, it was because earlier to this code we had a spurious _amazonCognitoIdentityProvider.AdminCreateUserAsync running, which created the identical user with the to-be email address. Then when the code above came along it naturally found that the to-be email address already existed in the user pool and rightly threw the AliasExistsException. But to the outside observer it looked like running the code caused the extra user along with the exception being thrown.

In conclusion, AWS Cognito functionality works exactly as expected and our team has fixed our codebase to remove the spurious _amazonCognitoIdentityProvider.AdminCreateUserAsync call while eating humble pie.

answered a month ago
-1

Based on the information provided and AWS Cognito's behavior with alias attributes, the issue you're experiencing is likely related to how you're setting the email_verified attribute in your code.

When you update an email address that serves as an alias in Amazon Cognito, the service has specific behavior around verification. The key issue in your code is this line:

new AttributeType { Name = "email_verified", Value = true }

You're passing a boolean true value, but the email_verified attribute expects a string value. It should be:

new AttributeType { Name = "email_verified", Value = "true" }

Notice the quotes around "true" - it needs to be a string representation of the boolean value.

When Cognito receives an improperly formatted value for email_verified, it may fail to properly process the email update, leading to the AliasExistsException even when the email address doesn't actually exist in the user pool. This is because the service is trying to validate the alias but encountering an error in processing the verification status.

The reason your AWS CLI command works is that the CLI automatically handles the type conversion correctly, passing Value=true as the string "true" to the API.

Additionally, when updating an email address that serves as an alias, you should be aware that:

  • The new email value must be unique across the user pool
  • If "Keep original attribute value active when an update is pending" is disabled (as in your case), the email should update immediately but may require verification depending on your pool settings
  • The verification process may be triggered by your Custom Email Sender Lambda trigger

Try updating your code to pass string values for the email_verified attribute, and this should resolve the AliasExistsException you're encountering.
Sources
Working with user attributes - Amazon Cognito
VerifyUserAttribute - Amazon Cognito User Pools

answered a month ago
EXPERT
reviewed a month ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.