Search code examples
asp.net-mvcmulti-tenantasp.net-identity-2

Duplicate Role Names on Asp.Net Identity and Multi-tenancy


I'm developing a Multi-tenancy web application with ASP.Net MVC and Identity 2.0. I have extended the IdentityRole like this:

public class ApplicationRole : IdentityRole
{
    public ApplicationRole() : base() { }
    public ApplicationRole(string name) : base(name) { }

    public string TenantId { get; set; }
}

This because each Tenant will have individual sets of roles, like "Admin", "Staff", etc.

But the problem is, when I add a new Role, if the "Tenant A" has "Admin" role, when I add to "Tenant B" the "Admin" role, I get an IdentityResult error because "Admin" name is taken... Its is kinda obvious because the "Name" field on the AspNetRoles table is Unique...

IdentityResult roleResult = await RoleManager.CreateAsync(
  new ApplicationRole
  {
    Name = "Admin",
    TenantId = GetTenantId()
  });

But then how I can customize ASP.Net Identity so the "Name" field in the "AspNetRoles" can be unique with "TenantId", and not alone? I found info about extend the IdentityRole (like I did adding a field), but not about change it or replace it...


Solution

  • Simply alter database's schema on ApplicationDbContext's OnModelCreating method like this:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        var role = modelBuilder.Entity<IdentityRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(
                new IndexAttribute("RoleNameIndex") 
                { IsUnique = false }));
    }
    

    But you must customize RoleValidator class also. Because default role validator invalidates duplicated role names.

    public class MyRoleValidator:RoleValidator<ApplicationRole>
    {
         public override async Task<IdentityResult> ValidateAsync(ApplicationRole item)
         {
             // implement your validation logic here
    
             return IdentityResult.Success;
         }
    }
    

    Now every time you create the role manager you must set the role validator.

    roleManager.RoleValidator=new MyRoleValidator();