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?
I made a mistake in translating/simplifying our code for the post, but yes our code is providing the string value of "true" for the "email_verified" attribute. We are not passing in a boolean.
I ended up posting another question which is a duplicate (because upon posting there's an error that makes it seem like the question didn't get posted). In that question I have it correct with the attribute value being "true" - https://repost.aws/questions/QUxWJmTDpYRMaFfih4iFN7cg/aws-net-sdk-returns-aliasexistsexception-incorrectly.