Hi, we are using these latest versions of AWS SDK Nuget packages that relate to AWS Cognito:
Our user pool is set up like this:
- Cognito user pool sign-in options: Email
- Password sign-in only
- Attributes to verify: Send email message, verify email address
- Required attributes:
email, given_name, family_name
- Self-service sign-up: Enabled
- Custom Email Sender Lambda trigger and Custom SMS Sender Lambda trigger have both been defined
So email attribute is an alias for the username.
Users can self sign-up on our web app (which uses AWS Amplify) or users can be created on our back-end which is written in .net 8.0 and uses the AWS SDK's admin methods.
We are trying to build a feature to allow email address to be updated. Currently we are only allowing this via the back-end which uses the following AWS SDK methods for the email address change:
IAmazonCognitoIdentityProvider.AdminGetUserAsync
IAmazonCognitoIdentityProviderAdminUpdateUserAttributesAsync
The problem we are facing is that whenever we try to call IAmazonCognitoIdentityProviderAdminUpdateUserAttributesAsync for an email address change we get an AliasExistsException with the message An account with the given email already exists. This happens even when no user with the updated email address exists in the user pool.
Apart from getting this exception we also see a 2nd user created with the updated email address and the original user with the old email address remains. We have confirmed that it's a new user by looking at the sub attribute and created/updated timestamps.
When we try the same thing via the AWS Console or AWS CLI it seems to work fine.
Here is what our code is effectively doing. The actual code is not exactly like this, this is a simplified version but the AWS calls are 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 = "email", Value = updatedEmailAddress });
clientMetadata.Add("PreviousEmailAddress", currentEmailAddress);
}
if (!string.IsNullOrWhiteSpace(updatedPhoneNumber))
{
userAttributeTypes.Add(
new AttributeType { Name = "phone_number", 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 is the AWS CLI call which works as expected and looks to be doing the same thing as what we're doing through the AWS SDK.
aws --profile <awsProfile> cognito-idp admin-update-user-attributes \
--user-pool-id <userPoolId> \
--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 here? Or is there a bug in the .Net AWS SDK?
Thanks for any help you can provide.
This is a duplicate of https://repost.aws/questions/QUVTjfSY2lT0W7Mt7VfMJv-w/aws-cognito-returns-aliasexistsexception-incorrectly - because upon posting the question I got an error and it didn't look like the question was posted.
This has also been raised as an issue on the AWS .Net SDK Github repo. Unfortunately they're not really interested in investigating this and are just pointing the finger back at AWS core. https://github.com/aws/aws-sdk-net/issues/4387