In Asp.Net Core Identity framework, I can easily require a unique email address by setting RequireUniqueEmail = true.
Is there any way to do the same for the user's phone number? Note that I don't want to require a confirmed phone number to sign in. The user is not required to enter a phone number but if they do, it must be unique.
You can try this, basically enforce it at the Db level first, followed by implementing proper checks at the Manager level.
At DbContext, I declared the indexing and uniqueness of both the username and email properties.
// ================== Customizing IdentityCore Tables ================== //
builder.Entity<User>().ToTable("Users").Property(p => p.Id).HasColumnName("Id").ValueGeneratedOnAdd();
builder.Entity<User>(entity =>
{
entity.HasIndex(u => u.UserName).IsUnique();
entity.HasIndex(u => u.NormalizedUserName).IsUnique();
entity.HasIndex(u => u.Email).IsUnique();
entity.HasIndex(u => u.NormalizedEmail).IsUnique();
entity.Property(u => u.Rating).HasDefaultValue(0).IsRequired();
entity.HasMany(u => u.UserRoles).WithOne(ur => ur.User)
.HasForeignKey(ur => ur.UserId).OnDelete(DeleteBehavior.Restrict);
entity.HasMany(u => u.UserClaims).WithOne(uc => uc.User)
.HasForeignKey(uc => uc.UserId).OnDelete(DeleteBehavior.Restrict);
});
And for the Manager level code:
/// <summary>
/// Sets the <paramref name="email"/> address for a <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose email should be set.</param>
/// <param name="email">The email to set.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/>
/// of the operation.
/// </returns>
public override async Task<IdentityResult> SetEmailAsync(User user, string email)
{
var dupeUser = await FindByEmailAsync(email);
if (dupeUser != null)
{
return IdentityResult.Failed(new IdentityError() {
Code = "DuplicateEmailException", // Wrong practice, lets set some beautiful code values in the future
Description = "An existing user with the new email already exists."
});
}
// Perform dupe checks
// Code that runs in SetEmailAsync
// Adapted from: aspnet/Identity/blob/dev/src/Core/UserManager.cs
//
// ThrowIfDisposed();
// var store = GetEmailStore();
// if (user == null)
// {
// throw new ArgumentNullException(nameof(user));
// }
// await store.SetEmailAsync(user, email, CancellationToken);
// await store.SetEmailConfirmedAsync(user, false, CancellationToken);
// await UpdateSecurityStampInternal(user);
//return await UpdateUserAsync(user);
return await base.SetEmailAsync(user, email);
}
This way, we retain .NET Core Identity Code's Integrity while enforcing the uniqueness of the property/properties that we want.
Note that the samples above are for email as of now. Simply do the same and then instead of modifying SetEmailAsync, work on SetPhoneNumberAsync at UserManager.cs.