Question

I was reading about user Identity in Microsoft and trying to apply them in my MVC5 app.

Up to my knowledge the Id is the key, while the userName is not key and the definition says that it can be null, so I was asking myself... why in the MVC5 project template, when you enter an already existing userName you will receive an Error message ??

I tried to reach to the userName validation but I couldn't.

Here is the database definition:

CREATE TABLE [dbo].[AspNetUsers] (
    [Id]            NVARCHAR (128) NOT NULL,
    [UserName]      NVARCHAR (MAX) NULL,

and here is the IdentityUser definition, notice (no validation):

namespace Microsoft.AspNet.Identity.EntityFramework
{
    public class IdentityUser : IUser
    {
        public IdentityUser();
        public IdentityUser(string userName);

        public virtual ICollection<IdentityUserClaim> Claims { get; }
        public virtual string Id { get; set; }
        public virtual ICollection<IdentityUserLogin> Logins { get; }
        public virtual string PasswordHash { get; set; }
        public virtual ICollection<IdentityUserRole> Roles { get; }
        public virtual string SecurityStamp { get; set; }
        public virtual string UserName { get; set; }
    }
}

and on registration, the UserManager.CreateAsync method is called, here is the definition:

     public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser() { UserName = model.UserName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

and this is the last thing I reach regarding CreateAsync:

public virtual Task<IdentityResult> CreateAsync(TUser user, string password);

I don't see validation anywhere in the code, however it won't allow you to enter an existing userName.

I think understanding how does this works will improve my experience with the Identity concept of asp.net and will improve my code.

Any guidance is highly appreciated

Was it helpful?

Solution

This happens in IdentityDbContext<TUser>, which your ApplicationDbContext probably inherits from. It overrides DbContext's ValidateEntity method to do the check. See this decompiled code:

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        if ((entityEntry != null) && (entityEntry.State == EntityState.Added))
        {
            TUser user = entityEntry.Entity as TUser;
            if ((user != null) && this.Users.Any<TUser>(u => string.Equals(u.UserName, user.UserName)))
            {
                return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("User", string.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, new object[] { user.UserName })) } };
            }
            IdentityRole role = entityEntry.Entity as IdentityRole;
            if ((role != null) && this.Roles.Any<IdentityRole>(r => string.Equals(r.Name, role.Name)))
            {
                return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("Role", string.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, new object[] { role.Name })) } };
            }
        }
        return base.ValidateEntity(entityEntry, items);
    }

If you don't want this behavior you can inherit from DbContext directly.

OTHER TIPS

When I'm looking at the example of ASP.NET Identity (https://www.nuget.org/packages/Microsoft.AspNet.Identity.Samples) I noticed that they use a UserValidator which is default set to RequireUniqueEmail = true;

The example uses the following code to set the RequireUniqueEmail property to true.

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };
        return manager;
    }

Perhaps this is the reason that the username is unique in your MVC application. Try setting the property to false!?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top